public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841]
@ 2023-02-18 21:42 Jason Merrill
  2023-02-18 21:42 ` [PATCH 2/3] c++: fix alias CTAD [PR105841] Jason Merrill
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Jason Merrill @ 2023-02-18 21:42 UTC (permalink / raw)
  To: gcc-patches

Tested x86_64-pc-linux-gnu.  Since this is fixing experimental (C++20)
functionality, I think it's reasonable to apply now; I'm interested in other
opinions, and thoughts about the user-facing functionality.  I'm thinking to
make it internal-only for GCC 13 at least by adding a space in the name, but
does this look useful to the library?

-- 8< --

C++20 class template argument deduction for an alias template involves
adding a constraint that the template arguments for the alias template can
be deduced from the return type of the deduction guide for the underlying
class template.  In the standard, this is modeled as defining a class
template with a partial specialization, but it's much more efficient to
implement with a trait that directly tries to perform the deduction.

The first argument to the trait is a template rather than a type, so various
places needed to be adjusted to accommodate that.

	PR c++/105841

gcc/ChangeLog:

	* doc/extend.texi (Type Traits):: Document __is_deducible.

gcc/cp/ChangeLog:

	* cp-trait.def (IS_DEDUCIBLE): New.
	* cxx-pretty-print.cc (pp_cxx_trait): Handle non-type.
	* parser.cc (cp_parser_trait): Likewise.
	* pt.cc (tsubst_copy_and_build): Likewise.
	(type_targs_deducible_from): New.
	(alias_ctad_tweaks): Use it.
	* semantics.cc (trait_expr_value): Handle CPTK_IS_DEDUCIBLE.
	(finish_trait_expr): Likewise.
	* constraint.cc (diagnose_trait_expr): Likewise.
	* cp-tree.h (type_targs_deducible_from): Declare.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/is_deducible1.C: New test.
---
 gcc/doc/extend.texi                      |  4 +++
 gcc/cp/cp-tree.h                         |  1 +
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cxx-pretty-print.cc               |  5 +++-
 gcc/cp/parser.cc                         | 20 +++++++++++---
 gcc/cp/pt.cc                             | 35 +++++++++++++++++-------
 gcc/cp/semantics.cc                      | 11 ++++++++
 gcc/testsuite/g++.dg/ext/is_deducible1.C | 27 ++++++++++++++++++
 gcc/cp/cp-trait.def                      |  1 +
 9 files changed, 92 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_deducible1.C

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 1ae68b0f20a..898701424ad 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -25207,6 +25207,10 @@ type.  A diagnostic is produced if this requirement is not met.
 If @code{type} is a cv-qualified class type, and not a union type
 ([basic.compound]) the trait is @code{true}, else it is @code{false}.
 
+@item __is_deducible (template, type)
+If template arguments for @code{template} can be deduced from
+@code{type} or obtained from default template arguments.
+
 @item __is_empty (type)
 If @code{__is_class (type)} is @code{false} then the trait is @code{false}.
 Otherwise @code{type} is considered empty if and only if: @code{type}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5595335bbf7..e79150ca4d8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7372,6 +7372,7 @@ extern tree fn_type_unification			(tree, tree, tree,
 						 bool, bool);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
+extern bool type_targs_deducible_from		(tree, tree);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 9374327008b..a28c85178fe 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3797,6 +3797,9 @@ diagnose_trait_expr (tree expr, tree args)
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (copy-initialization)", t1, t2);
       break;
+    case CPTK_IS_DEDUCIBLE:
+      inform (loc, "  %qD is not deducible from %qT", t1, t2);
+      break;
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index bea52a608f1..4ebd957decd 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -2626,7 +2626,10 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t)
     }
 
   pp_cxx_left_paren (pp);
-  pp->type_id (type1);
+  if (DECL_P (type1))
+    pp->expression (type1);
+  else
+    pp->type_id (type1);
   if (type2)
     {
       if (TREE_CODE (type2) != TREE_LIST)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 1a124f5395e..68950cace78 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -10960,10 +10960,22 @@ cp_parser_trait (cp_parser* parser, enum rid keyword)
   matching_parens parens;
   parens.require_open (parser);
 
-  {
-    type_id_in_expr_sentinel s (parser);
-    type1 = cp_parser_type_id (parser);
-  }
+  if (kind == CPTK_IS_DEDUCIBLE)
+    {
+      const cp_token* token = cp_lexer_peek_token (parser->lexer);
+      type1 = cp_parser_id_expression (parser,
+				       /*template_keyword_p=*/false,
+				       /*check_dependency_p=*/true,
+				       nullptr,
+				       /*declarator_p=*/false,
+				       /*optional_p=*/false);
+      type1 = cp_parser_lookup_name_simple (parser, type1, token->location);
+    }
+  else
+    {
+      type_id_in_expr_sentinel s (parser);
+      type1 = cp_parser_type_id (parser);
+    }
 
   if (type1 == error_mark_node)
     return error_mark_node;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index b1ac7d4beb4..2aa06557b99 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21577,8 +21577,9 @@ tsubst_copy_and_build (tree t,
 
     case TRAIT_EXPR:
       {
-	tree type1 = tsubst (TRAIT_EXPR_TYPE1 (t), args,
-			     complain, in_decl);
+	tree type1 = TRAIT_EXPR_TYPE1 (t);
+	if (TREE_CODE (type1) != TEMPLATE_DECL)
+	  type1 = tsubst (type1, args, complain, in_decl);
 	tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
 			     complain, in_decl);
 	RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
@@ -29979,7 +29980,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
   /* 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.
+     2) [fixed] 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
@@ -30079,7 +30080,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	      /* 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, atype, ret);
+	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
 	      ci = append_constraint (ci, same);
 	    }
 
@@ -30093,12 +30094,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	{
 	  /* 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)
+	  if (!type_targs_deducible_from (tmpl, ret))
 	    continue;
 	}
 
@@ -30108,6 +30104,25 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
   return aguides;
 }
 
+/* True iff template arguments for TMPL can be deduced from TYPE.
+   Used to implement CPTK_IS_DEDUCIBLE for alias CTAD.  */
+
+bool
+type_targs_deducible_from (tree tmpl, tree type)
+{
+  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+  int len = TREE_VEC_LENGTH (tparms);
+  tree targs = make_tree_vec (len);
+  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
+	     UNIFY_ALLOW_NONE, false))
+    return false;
+  /* Maybe add in default template args.  */
+  targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
+  if (targs == error_mark_node)
+    return false;
+  return constraints_satisfied_p (tmpl, targs);
+}
+
 /* Return artificial deduction guides built from the constructors of class
    template TMPL.  */
 
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 79b7cc72f21..9103f5de2f4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12048,6 +12048,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/false);
 
+    case CPTK_IS_DEDUCIBLE:
+      return type_targs_deducible_from (type1, type2);
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12205,6 +12208,14 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 	return error_mark_node;
       break;
 
+    case CPTK_IS_DEDUCIBLE:
+      if (!DECL_TYPE_TEMPLATE_P (type1))
+	{
+	  error ("%qD is not a class or alias template", type1);
+	  return error_mark_node;
+	}
+      break;
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
diff --git a/gcc/testsuite/g++.dg/ext/is_deducible1.C b/gcc/testsuite/g++.dg/ext/is_deducible1.C
new file mode 100644
index 00000000000..857f59db4c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_deducible1.C
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++20 } }
+
+template <class T> struct A { };
+template <class T> struct B { };
+
+// Simple forms.
+static_assert (__is_deducible (::A, A<int>));
+static_assert (__is_deducible (B, B<int>));
+static_assert (!__is_deducible (A, B<int>));
+static_assert (!__is_deducible (::B, A<int>));
+
+// This is the interesting use case for alias CTAD.
+template <class T> using AP = A<T*>;
+static_assert (__is_deducible (AP, A<int*>));
+static_assert (!__is_deducible (AP, A<int>));
+
+// Can't deduce a parameter not used on the RHS.
+template <class T> using C = void;
+static_assert (!__is_deducible (C, C<int>));
+
+// But a default template argument counts.
+template <class T = void> using D = void;
+static_assert (__is_deducible (D, D<int>));
+
+// We don't try to support this.
+template <class T> void f(T);
+bool b = __is_deducible (f, void (int)); // { dg-error "class or alias" }
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 823899a26c5..e43fb464f42 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible", 2)
 
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)

base-commit: 9944ca17c0766623bce260684edc614def7ea761
-- 
2.31.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/3] c++: fix alias CTAD [PR105841]
  2023-02-18 21:42 [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Jason Merrill
@ 2023-02-18 21:42 ` Jason Merrill
  2023-02-18 21:42 ` [PATCH 3/3] c++: CTAD for less-specialized alias template [PR102529] Jason Merrill
  2023-02-20 16:58 ` [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Patrick Palka
  2 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2023-02-18 21:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: Michael Spertus

In my initial implementation of alias CTAD, I described a couple of
differences from the specification that I thought would not have a practical
effect; this testcase demonstrates that I was wrong.  One difference is
resolved by the CPTK_IS_DEDUCIBLE commit; the other (adding too many of the
alias template parameters to the new deduction guide) is fixed by this
patch.

	PR c++/105841

gcc/cp/ChangeLog:

	* pt.cc	(corresponding_template_parameter_list): Split out...
	(corresponding_template_parameter): ...from here.
	(find_template_parameters): Factor out...
	(find_template_parameter_info::find_in): ...this function.
	(find_template_parameter_info::find_in_recursive): New.
	(find_template_parameter_info::found): New.
	(alias_ctad_tweaks): Only add parms used in the deduced args.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/class-deduction-alias14.C: New test.

Co-authored-by: Michael Spertus <mike@spertus.com>
---
 gcc/cp/pt.cc                                  | 133 +++++++++++++-----
 .../g++.dg/cpp2a/class-deduction-alias14.C    |  13 ++
 2 files changed, 114 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias14.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 2aa06557b99..1934c9dafac 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -10367,7 +10367,7 @@ lookup_and_finish_template_variable (tree templ, tree targs,
    return NULL_TREE.  */
 
 static tree
-corresponding_template_parameter (tree parms, int level, int index)
+corresponding_template_parameter_list (tree parms, int level, int index)
 {
   while (TMPL_PARMS_DEPTH (parms) > level)
     parms = TREE_CHAIN (parms);
@@ -10376,7 +10376,30 @@ corresponding_template_parameter (tree parms, int level, int index)
       || TREE_VEC_LENGTH (TREE_VALUE (parms)) <= index)
     return NULL_TREE;
 
-  tree t = TREE_VALUE (TREE_VEC_ELT (TREE_VALUE (parms), index));
+  return TREE_VEC_ELT (TREE_VALUE (parms), index);
+}
+
+/* Return the TREE_LIST for the template parameter from PARMS that positionally
+   corresponds to the template parameter PARM, or else return NULL_TREE.  */
+
+static tree
+corresponding_template_parameter_list (tree parms, tree parm)
+{
+  int level, index;
+  template_parm_level_and_index (parm, &level, &index);
+  return corresponding_template_parameter_list (parms, level, index);
+}
+
+/* As above, but pull out the actual parameter.  */
+
+static tree
+corresponding_template_parameter (tree parms, tree parm)
+{
+  tree list = corresponding_template_parameter_list (parms, parm);
+  if (!list)
+    return NULL_TREE;
+
+  tree t = TREE_VALUE (list);
   /* As in template_parm_to_arg.  */
   if (TREE_CODE (t) == TYPE_DECL || TREE_CODE (t) == TEMPLATE_DECL)
     t = TREE_TYPE (t);
@@ -10386,18 +10409,6 @@ corresponding_template_parameter (tree parms, int level, int index)
   gcc_assert (TEMPLATE_PARM_P (t));
   return t;
 }
-
-/* Return the template parameter from PARMS that positionally corresponds
-   to the template parameter PARM, or else return NULL_TREE.  */
-
-static tree
-corresponding_template_parameter (tree parms, tree parm)
-{
-  int level, index;
-  template_parm_level_and_index (parm, &level, &index);
-  return corresponding_template_parameter (parms, level, index);
-}
-
 \f
 struct pair_fn_data
 {
@@ -10670,6 +10681,11 @@ struct find_template_parameter_info
   tree *parm_list_tail = &parm_list;
   tree ctx_parms;
   int max_depth;
+
+  tree find_in (tree);
+  tree find_in_recursive (tree);
+  bool found (tree);
+  unsigned num_found () { return parms.elements (); }
 };
 
 /* Appends the declaration of T to the list in DATA.  */
@@ -10812,6 +10828,52 @@ any_template_parm_r (tree t, void *data)
   return 0;
 }
 
+/* Look through T for template parameters.  */
+
+tree
+find_template_parameter_info::find_in (tree t)
+{
+  return for_each_template_parm (t, keep_template_parm, this, &visited,
+				 /*include_nondeduced*/true,
+				 any_template_parm_r);
+}
+
+/* As above, but also recursively look into the default arguments of template
+   parameters we found.  Used for alias CTAD.  */
+
+tree
+find_template_parameter_info::find_in_recursive (tree t)
+{
+  if (tree r = find_in (t))
+    return r;
+  /* Since newly found parms are added to the end of the list, we
+     can just walk it until we reach the end.  */
+  for (tree pl = parm_list; pl; pl = TREE_CHAIN (pl))
+    {
+      tree parm = TREE_VALUE (pl);
+      tree list = corresponding_template_parameter_list (ctx_parms, parm);
+      if (tree r = find_in (TREE_PURPOSE (list)))
+	return r;
+    }
+  return NULL_TREE;
+}
+
+/* True if PARM was found by a previous call to find_in.  PARM can be a
+   TREE_LIST, a DECL_TEMPLATE_PARM_P, or a TEMPLATE_PARM_P.  */
+
+bool
+find_template_parameter_info::found (tree parm)
+{
+  if (TREE_CODE (parm) == TREE_LIST)
+    parm = TREE_VALUE (parm);
+  if (TREE_CODE (parm) == TYPE_DECL)
+    parm = TREE_TYPE (parm);
+  else
+    parm = DECL_INITIAL (parm);
+  gcc_checking_assert (TEMPLATE_PARM_P (parm));
+  return parms.contains (parm);
+}
+
 /* Returns a list of unique template parameters found within T, where CTX_PARMS
    are the template parameters in scope.  */
 
@@ -10822,8 +10884,7 @@ find_template_parameters (tree t, tree ctx_parms)
     return NULL_TREE;
 
   find_template_parameter_info ftpi (ctx_parms);
-  for_each_template_parm (t, keep_template_parm, &ftpi, &ftpi.visited,
-			  /*include_nondeduced*/true, any_template_parm_r);
+  ftpi.find_in (t);
   return ftpi.parm_list;
 }
 
@@ -29977,22 +30038,11 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
      * 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) [fixed] 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));
+  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
+  tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
   unsigned natparms = TREE_VEC_LENGTH (atparms);
   tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
   for (ovl_iterator iter (uguides); iter; ++iter)
@@ -30022,16 +30072,27 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  for (unsigned i = 0; i < len; ++i)
 	    if (TREE_VEC_ELT (targs, i) == NULL_TREE)
 	      ++ndlen;
-	  tree gtparms = make_tree_vec (natparms + ndlen);
+	  find_template_parameter_info ftpi (fullatparms);
+	  ftpi.find_in_recursive (targs);
+	  unsigned nusedatparms = ftpi.num_found ();
+	  unsigned nfparms = nusedatparms + ndlen;
+	  tree gtparms = make_tree_vec (nfparms);
 
 	  /* Set current_template_parms as in build_deduction_guide.  */
 	  auto ctp = make_temp_override (current_template_parms);
 	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
 	  TREE_VALUE (current_template_parms) = gtparms;
 
+	  j = 0;
 	  /* First copy over the parms of A.  */
-	  for (j = 0; j < natparms; ++j)
-	    TREE_VEC_ELT (gtparms, j) = TREE_VEC_ELT (atparms, j);
+	  for (unsigned i = 0; i < natparms; ++i)
+	    {
+	      tree elt = TREE_VEC_ELT (atparms, i);
+	      if (ftpi.found (elt))
+		TREE_VEC_ELT (gtparms, j++) = elt;
+	    }
+	  gcc_checking_assert (j == nusedatparms);
+
 	  /* Now rewrite the non-deduced parms of f.  */
 	  for (unsigned i = 0; ndlen && i < len; ++i)
 	    if (TREE_VEC_ELT (targs, i) == NULL_TREE)
@@ -30058,6 +30119,13 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	    }
 	  if (g == error_mark_node)
 	    continue;
+	  if (nfparms == 0)
+	    {
+	      /* The targs are all non-dependent, so g isn't a template.  */
+	      fprime = g;
+	      ret = TREE_TYPE (TREE_TYPE (fprime));
+	      goto non_template;
+	    }
 	  DECL_USE_TEMPLATE (g) = 0;
 	  fprime = build_template_decl (g, gtparms, false);
 	  DECL_TEMPLATE_RESULT (fprime) = g;
@@ -30094,6 +30162,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	{
 	  /* For a non-template deduction guide, if the arguments of A aren't
 	     deducible from the return type, don't add the candidate.  */
+	non_template:
 	  if (!type_targs_deducible_from (tmpl, ret))
 	    continue;
 	}
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias14.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias14.C
new file mode 100644
index 00000000000..22b96bcd5d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias14.C
@@ -0,0 +1,13 @@
+// PR c++/105841
+// { dg-do compile { target c++20 } }
+
+template<class T, int N>
+struct A { A(...); };
+
+template<class T, class... Ts>
+A(T, Ts...) -> A<T, sizeof...(Ts)>;
+
+template<class T, int N=0>
+using B = A<T, N>;
+
+B b(0, 0);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 3/3] c++: CTAD for less-specialized alias template [PR102529]
  2023-02-18 21:42 [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Jason Merrill
  2023-02-18 21:42 ` [PATCH 2/3] c++: fix alias CTAD [PR105841] Jason Merrill
@ 2023-02-18 21:42 ` Jason Merrill
  2023-02-20 16:58 ` [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Patrick Palka
  2 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2023-02-18 21:42 UTC (permalink / raw)
  To: gcc-patches

The standard was unclear what happens with the transformation of a deduction
guide if the initial template argument deduction fails for a reason other
than not deducing all the arguments; my implementation assumed that the
right thing was to give up on the deduction guide.  But in consideration of
CWG2664 this week I realized that we get a better result by just continuing
with an empty set of deductions, so the alias deduction guide is the same as
the original deduction guide plus the deducible constraint.

	DR 2664
	PR c++/102529

gcc/cp/ChangeLog:

	* pt.cc (alias_ctad_tweaks): Continue after deduction failure.

gcc/testsuite/ChangeLog:

	* g++.dg/DRs/dr2664.C: New test.
	* g++.dg/cpp2a/class-deduction-alias15.C: New test.
---
 gcc/cp/pt.cc                                   |  4 +++-
 gcc/testsuite/g++.dg/DRs/dr2664.C              | 17 +++++++++++++++++
 .../g++.dg/cpp2a/class-deduction-alias15.C     | 18 ++++++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2664.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias15.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 1934c9dafac..69391450b75 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -30063,7 +30063,9 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  tree targs = make_tree_vec (len);
 	  int err = unify (ftparms, targs, ret, utype, UNIFY_ALLOW_NONE, false);
 	  if (err)
-	    continue;
+	    /* CWG2664: Discard any deductions, still build the guide.  */
+	    for (unsigned i = 0; i < len; ++i)
+	      TREE_VEC_ELT (targs, i) = NULL_TREE;
 
 	  /* The number of parms for f' is the number of parms for A plus
 	     non-deduced parms of f.  */
diff --git a/gcc/testsuite/g++.dg/DRs/dr2664.C b/gcc/testsuite/g++.dg/DRs/dr2664.C
new file mode 100644
index 00000000000..f6bf8e2ecef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2664.C
@@ -0,0 +1,17 @@
+// CWG 2664
+// { dg-do compile { target c++20 } }
+
+template <class S1, class S2> struct C {
+  C(...);
+};
+
+template<class T1> C(T1) -> C<T1, T1>;
+template<class T1, class T2> C(T1, T2) -> C<T1 *, T2>;
+
+template<class V1, class V2> using A = C<V1, V2>;
+
+C c1{""};
+A a1{""};
+
+C c2{"", 1};
+A a2{"", 1};
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias15.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias15.C
new file mode 100644
index 00000000000..db615faf8e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias15.C
@@ -0,0 +1,18 @@
+// PR c++/102529
+// { dg-do compile { target c++20 } }
+
+template <typename T>
+struct C {
+    template <typename U>
+    C(U);
+};
+
+template <typename U>
+C(U) -> C<U*>;
+
+template <typename T>
+    requires true
+using A = C<T>;
+
+C ok(1);   // ok, a is a C<int*>
+A bad(2);  // fails
-- 
2.31.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841]
  2023-02-18 21:42 [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Jason Merrill
  2023-02-18 21:42 ` [PATCH 2/3] c++: fix alias CTAD [PR105841] Jason Merrill
  2023-02-18 21:42 ` [PATCH 3/3] c++: CTAD for less-specialized alias template [PR102529] Jason Merrill
@ 2023-02-20 16:58 ` Patrick Palka
  2023-03-09 15:28   ` Jason Merrill
  2 siblings, 1 reply; 5+ messages in thread
From: Patrick Palka @ 2023-02-20 16:58 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Sat, 18 Feb 2023, Jason Merrill via Gcc-patches wrote:

> Tested x86_64-pc-linux-gnu.  Since this is fixing experimental (C++20)
> functionality, I think it's reasonable to apply now; I'm interested in other
> opinions, and thoughts about the user-facing functionality.  I'm thinking to
> make it internal-only for GCC 13 at least by adding a space in the name, but
> does this look useful to the library?

IIUC this looks like a generalization of an __is_specialization_of trait
that returns whether a type is a specialization of a given class template,
which seems potentially useful for the library to me.  We already define
some ad-hoc predicates for testing this, e.g. __is_reverse_view,
__is_span etc in <ranges> as well as a more general __is_specialization_of
in <format> for templates that take only type arguments.  Using a built-in
trait should be more efficient.

> 
> -- 8< --
> 
> C++20 class template argument deduction for an alias template involves
> adding a constraint that the template arguments for the alias template can
> be deduced from the return type of the deduction guide for the underlying
> class template.  In the standard, this is modeled as defining a class
> template with a partial specialization, but it's much more efficient to
> implement with a trait that directly tries to perform the deduction.
> 
> The first argument to the trait is a template rather than a type, so various
> places needed to be adjusted to accommodate that.
> 
> 	PR c++/105841
> 
> gcc/ChangeLog:
> 
> 	* doc/extend.texi (Type Traits):: Document __is_deducible.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def (IS_DEDUCIBLE): New.
> 	* cxx-pretty-print.cc (pp_cxx_trait): Handle non-type.
> 	* parser.cc (cp_parser_trait): Likewise.
> 	* pt.cc (tsubst_copy_and_build): Likewise.
> 	(type_targs_deducible_from): New.
> 	(alias_ctad_tweaks): Use it.
> 	* semantics.cc (trait_expr_value): Handle CPTK_IS_DEDUCIBLE.
> 	(finish_trait_expr): Likewise.
> 	* constraint.cc (diagnose_trait_expr): Likewise.
> 	* cp-tree.h (type_targs_deducible_from): Declare.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/is_deducible1.C: New test.
> ---
>  gcc/doc/extend.texi                      |  4 +++
>  gcc/cp/cp-tree.h                         |  1 +
>  gcc/cp/constraint.cc                     |  3 ++
>  gcc/cp/cxx-pretty-print.cc               |  5 +++-
>  gcc/cp/parser.cc                         | 20 +++++++++++---
>  gcc/cp/pt.cc                             | 35 +++++++++++++++++-------
>  gcc/cp/semantics.cc                      | 11 ++++++++
>  gcc/testsuite/g++.dg/ext/is_deducible1.C | 27 ++++++++++++++++++
>  gcc/cp/cp-trait.def                      |  1 +
>  9 files changed, 92 insertions(+), 15 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_deducible1.C
> 
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 1ae68b0f20a..898701424ad 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -25207,6 +25207,10 @@ type.  A diagnostic is produced if this requirement is not met.
>  If @code{type} is a cv-qualified class type, and not a union type
>  ([basic.compound]) the trait is @code{true}, else it is @code{false}.
>  
> +@item __is_deducible (template, type)
> +If template arguments for @code{template} can be deduced from
> +@code{type} or obtained from default template arguments.
> +
>  @item __is_empty (type)
>  If @code{__is_class (type)} is @code{false} then the trait is @code{false}.
>  Otherwise @code{type} is considered empty if and only if: @code{type}
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 5595335bbf7..e79150ca4d8 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7372,6 +7372,7 @@ extern tree fn_type_unification			(tree, tree, tree,
>  						 bool, bool);
>  extern void mark_decl_instantiated		(tree, int);
>  extern int more_specialized_fn			(tree, tree, int);
> +extern bool type_targs_deducible_from		(tree, tree);
>  extern void do_decl_instantiation		(tree, tree);
>  extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
>  extern bool always_instantiate_p		(tree);
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 9374327008b..a28c85178fe 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3797,6 +3797,9 @@ diagnose_trait_expr (tree expr, tree args)
>        inform (loc, "  %qT is not a reference that binds to a temporary "
>  	      "object of type %qT (copy-initialization)", t1, t2);
>        break;
> +    case CPTK_IS_DEDUCIBLE:
> +      inform (loc, "  %qD is not deducible from %qT", t1, t2);
> +      break;
>  #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
>      case CPTK_##CODE:
>  #include "cp-trait.def"
> diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
> index bea52a608f1..4ebd957decd 100644
> --- a/gcc/cp/cxx-pretty-print.cc
> +++ b/gcc/cp/cxx-pretty-print.cc
> @@ -2626,7 +2626,10 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t)
>      }
>  
>    pp_cxx_left_paren (pp);
> -  pp->type_id (type1);
> +  if (DECL_P (type1))
> +    pp->expression (type1);
> +  else
> +    pp->type_id (type1);

Since the first argument of a TRAIT_EXPR can now be a TEMPLATE_DECL, I
suppose cp_tree_equal needs to be changed too.

>    if (type2)
>      {
>        if (TREE_CODE (type2) != TREE_LIST)
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 1a124f5395e..68950cace78 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -10960,10 +10960,22 @@ cp_parser_trait (cp_parser* parser, enum rid keyword)
>    matching_parens parens;
>    parens.require_open (parser);
>  
> -  {
> -    type_id_in_expr_sentinel s (parser);
> -    type1 = cp_parser_type_id (parser);
> -  }
> +  if (kind == CPTK_IS_DEDUCIBLE)
> +    {
> +      const cp_token* token = cp_lexer_peek_token (parser->lexer);
> +      type1 = cp_parser_id_expression (parser,
> +				       /*template_keyword_p=*/false,
> +				       /*check_dependency_p=*/true,
> +				       nullptr,
> +				       /*declarator_p=*/false,
> +				       /*optional_p=*/false);
> +      type1 = cp_parser_lookup_name_simple (parser, type1, token->location);
> +    }
> +  else
> +    {
> +      type_id_in_expr_sentinel s (parser);
> +      type1 = cp_parser_type_id (parser);
> +    }
>  
>    if (type1 == error_mark_node)
>      return error_mark_node;
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index b1ac7d4beb4..2aa06557b99 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -21577,8 +21577,9 @@ tsubst_copy_and_build (tree t,
>  
>      case TRAIT_EXPR:
>        {
> -	tree type1 = tsubst (TRAIT_EXPR_TYPE1 (t), args,
> -			     complain, in_decl);
> +	tree type1 = TRAIT_EXPR_TYPE1 (t);
> +	if (TREE_CODE (type1) != TEMPLATE_DECL)
> +	  type1 = tsubst (type1, args, complain, in_decl);
>  	tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
>  			     complain, in_decl);
>  	RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
> @@ -29979,7 +29980,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>    /* 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.
> +     2) [fixed] 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
> @@ -30079,7 +30080,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  	      /* 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, atype, ret);
> +	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
>  	      ci = append_constraint (ci, same);
>  	    }
>  
> @@ -30093,12 +30094,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  	{
>  	  /* 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)
> +	  if (!type_targs_deducible_from (tmpl, ret))
>  	    continue;
>  	}
>  
> @@ -30108,6 +30104,25 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>    return aguides;
>  }
>  
> +/* True iff template arguments for TMPL can be deduced from TYPE.
> +   Used to implement CPTK_IS_DEDUCIBLE for alias CTAD.  */
> +
> +bool
> +type_targs_deducible_from (tree tmpl, tree type)
> +{
> +  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
> +  int len = TREE_VEC_LENGTH (tparms);
> +  tree targs = make_tree_vec (len);
> +  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
> +	     UNIFY_ALLOW_NONE, false))
> +    return false;
> +  /* Maybe add in default template args.  */
> +  targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
> +  if (targs == error_mark_node)
> +    return false;
> +  return constraints_satisfied_p (tmpl, targs);
> +}

For sake of the __is_specialization_of use case, I wonder if it'd
be possible to have a "fast path" that avoids deduction/coercion when
the given template is a class template?

> +
>  /* Return artificial deduction guides built from the constructors of class
>     template TMPL.  */
>  
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 79b7cc72f21..9103f5de2f4 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12048,6 +12048,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>      case CPTK_REF_CONVERTS_FROM_TEMPORARY:
>        return ref_xes_from_temporary (type1, type2, /*direct_init=*/false);
>  
> +    case CPTK_IS_DEDUCIBLE:
> +      return type_targs_deducible_from (type1, type2);
> +
>  #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
>      case CPTK_##CODE:
>  #include "cp-trait.def"
> @@ -12205,6 +12208,14 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>  	return error_mark_node;
>        break;
>  
> +    case CPTK_IS_DEDUCIBLE:
> +      if (!DECL_TYPE_TEMPLATE_P (type1))
> +	{
> +	  error ("%qD is not a class or alias template", type1);
> +	  return error_mark_node;
> +	}
> +      break;
> +
>  #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
>      case CPTK_##CODE:
>  #include "cp-trait.def"
> diff --git a/gcc/testsuite/g++.dg/ext/is_deducible1.C b/gcc/testsuite/g++.dg/ext/is_deducible1.C
> new file mode 100644
> index 00000000000..857f59db4c8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_deducible1.C
> @@ -0,0 +1,27 @@
> +// { dg-do compile { target c++20 } }
> +
> +template <class T> struct A { };
> +template <class T> struct B { };
> +
> +// Simple forms.
> +static_assert (__is_deducible (::A, A<int>));
> +static_assert (__is_deducible (B, B<int>));
> +static_assert (!__is_deducible (A, B<int>));
> +static_assert (!__is_deducible (::B, A<int>));
> +
> +// This is the interesting use case for alias CTAD.
> +template <class T> using AP = A<T*>;
> +static_assert (__is_deducible (AP, A<int*>));
> +static_assert (!__is_deducible (AP, A<int>));
> +
> +// Can't deduce a parameter not used on the RHS.
> +template <class T> using C = void;
> +static_assert (!__is_deducible (C, C<int>));
> +
> +// But a default template argument counts.
> +template <class T = void> using D = void;
> +static_assert (__is_deducible (D, D<int>));
> +
> +// We don't try to support this.
> +template <class T> void f(T);
> +bool b = __is_deducible (f, void (int)); // { dg-error "class or alias" }
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 823899a26c5..e43fb464f42 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
>  DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
>  DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
>  DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
> +DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible", 2)
>  
>  DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
>  DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
> 
> base-commit: 9944ca17c0766623bce260684edc614def7ea761
> -- 
> 2.31.1
> 
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841]
  2023-02-20 16:58 ` [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Patrick Palka
@ 2023-03-09 15:28   ` Jason Merrill
  0 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2023-03-09 15:28 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1638 bytes --]

On 2/20/23 11:58, Patrick Palka wrote:
> On Sat, 18 Feb 2023, Jason Merrill via Gcc-patches wrote:
> 
>> Tested x86_64-pc-linux-gnu.  Since this is fixing experimental (C++20)
>> functionality, I think it's reasonable to apply now; I'm interested in other
>> opinions, and thoughts about the user-facing functionality.  I'm thinking to
>> make it internal-only for GCC 13 at least by adding a space in the name, but
>> does this look useful to the library?
> 
> IIUC this looks like a generalization of an __is_specialization_of trait
> that returns whether a type is a specialization of a given class template,
> which seems potentially useful for the library to me.  We already define
> some ad-hoc predicates for testing this, e.g. __is_reverse_view,
> __is_span etc in <ranges> as well as a more general __is_specialization_of
> in <format> for templates that take only type arguments.  Using a built-in
> trait should be more efficient.
> 
>[...]
> 
> Since the first argument of a TRAIT_EXPR can now be a TEMPLATE_DECL, I
> suppose cp_tree_equal needs to be changed too.
>
 >[...]
> 
> For sake of the __is_specialization_of use case, I wonder if it'd
> be possible to have a "fast path" that avoids deduction/coercion when
> the given template is a class template?

Thanks, done.  I've also fixed array bounds type deduction and added 
more comments about the relationship of the implementation and the 
specification in terms of partial specialization.

The second patch makes it internal-only for GCC 13; you can revert that 
if you want to experiment with using it in the library.

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

[-- Attachment #2: 0001-c-add-__is_deducible-trait-PR105841.patch --]
[-- Type: text/x-patch, Size: 12864 bytes --]

From 81f820cff3316cea454ba81dc38ddf55b1afa852 Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Thu, 9 Feb 2023 12:51:51 -0800
Subject: [PATCH] c++: add __is_deducible trait [PR105841]
To: gcc-patches@gcc.gnu.org

C++20 class template argument deduction for an alias template involves
adding a constraint that the template arguments for the alias template can
be deduced from the return type of the deduction guide for the underlying
class template.  In the standard, this is modeled as defining a class
template with a partial specialization, but it's much more efficient to
implement with a trait that directly tries to perform the deduction.

The first argument to the trait is a template rather than a type, so various
places needed to be adjusted to accommodate that.

	PR c++/105841

gcc/ChangeLog:

	* doc/extend.texi (Type Traits):: Document __is_deducible.

gcc/cp/ChangeLog:

	* cp-trait.def (IS_DEDUCIBLE): New.
	* cxx-pretty-print.cc (pp_cxx_trait): Handle non-type.
	* parser.cc (cp_parser_trait): Likewise.
	* tree.cc (cp_tree_equal): Likewise.
	* pt.cc (tsubst_copy_and_build): Likewise.
	(type_targs_deducible_from): New.
	(alias_ctad_tweaks): Use it.
	* semantics.cc (trait_expr_value): Handle CPTK_IS_DEDUCIBLE.
	(finish_trait_expr): Likewise.
	* constraint.cc (diagnose_trait_expr): Likewise.
	* cp-tree.h (type_targs_deducible_from): Declare.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/is_deducible1.C: New test.
---
 gcc/doc/extend.texi                      |  4 ++
 gcc/cp/cp-tree.h                         |  1 +
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cxx-pretty-print.cc               |  5 +-
 gcc/cp/parser.cc                         | 20 +++++--
 gcc/cp/pt.cc                             | 69 ++++++++++++++++++++----
 gcc/cp/semantics.cc                      | 11 ++++
 gcc/cp/tree.cc                           |  2 +-
 gcc/testsuite/g++.dg/ext/is_deducible1.C | 31 +++++++++++
 gcc/cp/cp-trait.def                      |  1 +
 10 files changed, 131 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_deducible1.C

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index c1122916255..b64a85722db 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -25213,6 +25213,10 @@ type.  A diagnostic is produced if this requirement is not met.
 If @code{type} is a cv-qualified class type, and not a union type
 ([basic.compound]) the trait is @code{true}, else it is @code{false}.
 
+@item __is_deducible (template, type)
+If template arguments for @code{template} can be deduced from
+@code{type} or obtained from default template arguments.
+
 @item __is_empty (type)
 If @code{__is_class (type)} is @code{false} then the trait is @code{false}.
 Otherwise @code{type} is considered empty if and only if: @code{type}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fb21c064141..dfc1c845768 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7361,6 +7361,7 @@ extern tree fn_type_unification			(tree, tree, tree,
 						 bool, bool);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
+extern bool type_targs_deducible_from		(tree, tree);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 9374327008b..a28c85178fe 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3797,6 +3797,9 @@ diagnose_trait_expr (tree expr, tree args)
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (copy-initialization)", t1, t2);
       break;
+    case CPTK_IS_DEDUCIBLE:
+      inform (loc, "  %qD is not deducible from %qT", t1, t2);
+      break;
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index bea52a608f1..7f4556d0da2 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -2626,7 +2626,10 @@ pp_cxx_trait (cxx_pretty_printer *pp, tree t)
     }
 
   pp_cxx_left_paren (pp);
-  pp->type_id (type1);
+  if (TYPE_P (type1))
+    pp->type_id (type1);
+  else
+    pp->expression (type1);
   if (type2)
     {
       if (TREE_CODE (type2) != TREE_LIST)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index b00a6cd5b8b..533041946c0 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -10960,10 +10960,22 @@ cp_parser_trait (cp_parser* parser, enum rid keyword)
   matching_parens parens;
   parens.require_open (parser);
 
-  {
-    type_id_in_expr_sentinel s (parser);
-    type1 = cp_parser_type_id (parser);
-  }
+  if (kind == CPTK_IS_DEDUCIBLE)
+    {
+      const cp_token* token = cp_lexer_peek_token (parser->lexer);
+      type1 = cp_parser_id_expression (parser,
+				       /*template_keyword_p=*/false,
+				       /*check_dependency_p=*/true,
+				       nullptr,
+				       /*declarator_p=*/false,
+				       /*optional_p=*/false);
+      type1 = cp_parser_lookup_name_simple (parser, type1, token->location);
+    }
+  else
+    {
+      type_id_in_expr_sentinel s (parser);
+      type1 = cp_parser_type_id (parser);
+    }
 
   if (type1 == error_mark_node)
     return error_mark_node;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index aafc99d12c3..e87cda245b2 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21587,8 +21587,8 @@ tsubst_copy_and_build (tree t,
 
     case TRAIT_EXPR:
       {
-	tree type1 = tsubst (TRAIT_EXPR_TYPE1 (t), args,
-			     complain, in_decl);
+	tree type1 = tsubst_copy (TRAIT_EXPR_TYPE1 (t), args,
+				  complain, in_decl);
 	tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
 			     complain, in_decl);
 	RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
@@ -29989,7 +29989,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
   /* 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.
+     2) [fixed] 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
@@ -30089,7 +30089,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	      /* 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, atype, ret);
+	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
 	      ci = append_constraint (ci, same);
 	    }
 
@@ -30103,12 +30103,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	{
 	  /* 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)
+	  if (!type_targs_deducible_from (tmpl, ret))
 	    continue;
 	}
 
@@ -30118,6 +30113,60 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
   return aguides;
 }
 
+/* True iff template arguments for TMPL can be deduced from TYPE.
+   Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to
+   [over.match.class.deduct].
+
+   This check is specified in terms of partial specialization, so the behavior
+   should be parallel to that of get_partial_spec_bindings.  */
+
+bool
+type_targs_deducible_from (tree tmpl, tree type)
+{
+  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+  int len = TREE_VEC_LENGTH (tparms);
+  tree targs = make_tree_vec (len);
+  bool tried_array_deduction = (cxx_dialect < cxx17);
+
+  /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
+     specialization of TMPL.  */
+  if (DECL_CLASS_TEMPLATE_P (tmpl))
+    return (CLASS_TYPE_P (type)
+	    && CLASSTYPE_TEMPLATE_INFO (type)
+	    && CLASSTYPE_TI_TEMPLATE (type) == tmpl);
+
+  /* Otherwise it's an alias template.  */
+ again:
+  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
+	     UNIFY_ALLOW_NONE, false))
+    return false;
+
+  /* We don't fail on an undeduced targ the second time through (like
+     get_partial_spec_bindings) because we're going to try defaults.  */
+  if (!tried_array_deduction)
+    for (int i =  0; i < len; ++i)
+      if (! TREE_VEC_ELT (targs, i))
+	{
+	  try_array_deduction (tparms, targs, TREE_TYPE (tmpl));
+	  tried_array_deduction = true;
+	  if (TREE_VEC_ELT (targs, i))
+	    goto again;
+	}
+
+  /* Maybe add in default template args.  This seems like a flaw in the
+     specification in terms of partial specialization, since it says the
+     partial specialization has the the template parameter list of A, but a
+     partial specialization can't have default targs.  */
+  targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
+  if (targs == error_mark_node)
+    return false;
+
+  /* I believe we don't need the template_template_parm_bindings_ok_p call
+     because coerce_template_parms did coerce_template_template_parms.  */
+
+  return constraints_satisfied_p (tmpl, targs);
+}
+
 /* Return artificial deduction guides built from the constructors of class
    template TMPL.  */
 
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index db982d594e6..d67a9b26719 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12049,6 +12049,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/false);
 
+    case CPTK_IS_DEDUCIBLE:
+      return type_targs_deducible_from (type1, type2);
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12206,6 +12209,14 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 	return error_mark_node;
       break;
 
+    case CPTK_IS_DEDUCIBLE:
+      if (!DECL_TYPE_TEMPLATE_P (type1))
+	{
+	  error ("%qD is not a class or alias template", type1);
+	  return error_mark_node;
+	}
+      break;
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index cd0dd963532..de83d41e6b4 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -4235,7 +4235,7 @@ cp_tree_equal (tree t1, tree t2)
     case TRAIT_EXPR:
       if (TRAIT_EXPR_KIND (t1) != TRAIT_EXPR_KIND (t2))
 	return false;
-      return same_type_p (TRAIT_EXPR_TYPE1 (t1), TRAIT_EXPR_TYPE1 (t2))
+      return cp_tree_equal (TRAIT_EXPR_TYPE1 (t1), TRAIT_EXPR_TYPE1 (t2))
 	&& cp_tree_equal (TRAIT_EXPR_TYPE2 (t1), TRAIT_EXPR_TYPE2 (t2));
 
     case NON_LVALUE_EXPR:
diff --git a/gcc/testsuite/g++.dg/ext/is_deducible1.C b/gcc/testsuite/g++.dg/ext/is_deducible1.C
new file mode 100644
index 00000000000..30cbe089f50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_deducible1.C
@@ -0,0 +1,31 @@
+// { dg-do compile { target c++20 } }
+
+template <class T> struct A { };
+template <class T> struct B { };
+
+// Simple forms.
+static_assert (__is_deducible (::A, A<int>));
+static_assert (__is_deducible (B, B<int>));
+static_assert (!__is_deducible (A, B<int>));
+static_assert (!__is_deducible (::B, A<int>));
+
+// This is the interesting use case for alias CTAD.
+template <class T> using AP = A<T*>;
+static_assert (__is_deducible (AP, A<int*>));
+static_assert (!__is_deducible (AP, A<int>));
+
+// Can't deduce a parameter not used on the RHS.
+template <class T> using C = void;
+static_assert (!__is_deducible (C, C<int>));
+
+// But a default template argument counts.
+template <class T = void> using D = void;
+static_assert (__is_deducible (D, D<int>));
+
+// P0127 array bound type deduction should work here.
+template <class T, T N> using E = int[N];
+static_assert (__is_deducible (E, int[42]));
+
+// We don't try to support this.
+template <class T> void f(T);
+bool b = __is_deducible (f, void (int)); // { dg-error "class or alias" }
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 823899a26c5..e43fb464f42 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible", 2)
 
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
-- 
2.31.1


[-- Attachment #3: 0001-c-hide-__is_deducible-for-GCC-13.patch --]
[-- Type: text/x-patch, Size: 3216 bytes --]

From 4d4566c32e0c4dc988e883bd3a1c303b3b475bda Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Wed, 8 Mar 2023 23:53:21 -0500
Subject: [PATCH] c++: hide __is_deducible for GCC 13
To: gcc-patches@gcc.gnu.org

I want to have more discussion about the interface before claiming the
__is_deducible name, so for GCC 13 make it internal-only.

gcc/ChangeLog:

	* doc/extend.texi: Comment out __is_deducible docs.

gcc/cp/ChangeLog:

	* cp-trait.def (IS_DEDUCIBLE): Add space to name.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/is_deducible1.C: Guard with
	__has_builtin (__is_deducible).
---
 gcc/doc/extend.texi                      | 7 ++++---
 gcc/testsuite/g++.dg/ext/is_deducible1.C | 6 +++++-
 gcc/cp/cp-trait.def                      | 3 ++-
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b64a85722db..9dcfc860108 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -25213,9 +25213,10 @@ type.  A diagnostic is produced if this requirement is not met.
 If @code{type} is a cv-qualified class type, and not a union type
 ([basic.compound]) the trait is @code{true}, else it is @code{false}.
 
-@item __is_deducible (template, type)
-If template arguments for @code{template} can be deduced from
-@code{type} or obtained from default template arguments.
+@c FIXME Commented out for GCC 13, discuss user interface for GCC 14.
+@c @item __is_deducible (template, type)
+@c If template arguments for @code{template} can be deduced from
+@c @code{type} or obtained from default template arguments.
 
 @item __is_empty (type)
 If @code{__is_class (type)} is @code{false} then the trait is @code{false}.
diff --git a/gcc/testsuite/g++.dg/ext/is_deducible1.C b/gcc/testsuite/g++.dg/ext/is_deducible1.C
index 30cbe089f50..6873e4f4c2e 100644
--- a/gcc/testsuite/g++.dg/ext/is_deducible1.C
+++ b/gcc/testsuite/g++.dg/ext/is_deducible1.C
@@ -1,5 +1,7 @@
 // { dg-do compile { target c++20 } }
 
+#if __has_builtin (__is_deducible)
+
 template <class T> struct A { };
 template <class T> struct B { };
 
@@ -26,6 +28,8 @@ static_assert (__is_deducible (D, D<int>));
 template <class T, T N> using E = int[N];
 static_assert (__is_deducible (E, int[42]));
 
+#endif // __has_builtin (__is_deducible)
+
 // We don't try to support this.
 template <class T> void f(T);
-bool b = __is_deducible (f, void (int)); // { dg-error "class or alias" }
+bool b = __is_deducible (f, void (int)); // { dg-error "" }
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e43fb464f42..bac593c0094 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,7 +84,8 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
-DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible", 2)
+/* FIXME Added space to avoid direct usage in GCC 13.  */
+DEFTRAIT_EXPR (IS_DEDUCIBLE, "__is_deducible ", 2)
 
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
-- 
2.31.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-03-09 15:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-18 21:42 [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Jason Merrill
2023-02-18 21:42 ` [PATCH 2/3] c++: fix alias CTAD [PR105841] Jason Merrill
2023-02-18 21:42 ` [PATCH 3/3] c++: CTAD for less-specialized alias template [PR102529] Jason Merrill
2023-02-20 16:58 ` [PATCH RFC 1/3] c++: add __is_deducible trait [PR105841] Patrick Palka
2023-03-09 15:28   ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).