[PATCH 1/2] c++: Handle enumerator in C++20 alias CTAD. [PR96199]

Jason Merrill jason@redhat.com
Tue Aug 18 20:21:33 GMT 2020


To form a deduction guide for an alias template, we substitute the template
arguments from the pattern into the deduction guide for the underlying
class.  In the case of B(A1<X>), that produces B(A1<B<T,1>::X>) -> B<T,1>.
But since an enumerator doesn't have its own template info, and B<T,1> is a
dependent scope, trying to look up B<T,1>::X fails and we crash.  So we need
to produce a SCOPE_REF instead.

And trying to use the members of the template class is wrong for other
members, as well, as it gives a nonsensical result if the class is
specialized.

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

gcc/cp/ChangeLog:

	PR c++/96199
	* pt.c (maybe_dependent_member_ref): New.
	(tsubst_copy) [CONST_DECL]: Use it.
	[VAR_DECL]: Likewise.
	(tsubst_aggr_type): Handle nested type.

gcc/testsuite/ChangeLog:

	PR c++/96199
	* g++.dg/cpp2a/class-deduction-alias4.C: New test.
---
 gcc/cp/pt.c                                   | 43 ++++++++++++++++++
 .../g++.dg/cpp2a/class-deduction-alias4.C     | 44 +++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b80fe0a5cc5..585d944542b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13391,6 +13391,17 @@ tsubst_aggr_type (tree t,
 					 complain, in_decl);
 	  if (argvec == error_mark_node)
 	    r = error_mark_node;
+	  else if (cxx_dialect >= cxx20 && dependent_scope_p (context))
+	    {
+	      /* See maybe_dependent_member_ref.  */
+	      tree name = TYPE_IDENTIFIER (t);
+	      tree fullname = name;
+	      if (instantiates_primary_template_p (t))
+		fullname = build_nt (TEMPLATE_ID_EXPR, name,
+				     INNERMOST_TEMPLATE_ARGS (argvec));
+	      return build_typename_type (context, name, fullname,
+					  typename_type);
+	    }
 	  else
 	    {
 	      r = lookup_template_class (t, argvec, in_decl, context,
@@ -16313,6 +16324,32 @@ tsubst_init (tree init, tree decl, tree args,
   return init;
 }
 
+/* If T is a reference to a dependent member of the current instantiation C and
+   we are trying to refer to that member in a partial instantiation of C,
+   return a SCOPE_REF; otherwise, return NULL_TREE.
+
+   This can happen when forming a C++20 alias template deduction guide, as in
+   PR96199.  */
+
+static tree
+maybe_dependent_member_ref (tree t, tree args, tsubst_flags_t complain,
+			    tree in_decl)
+{
+  if (cxx_dialect < cxx20)
+    return NULL_TREE;
+
+  tree ctx = context_for_name_lookup (t);
+  if (!CLASS_TYPE_P (ctx))
+    return NULL_TREE;
+
+  ctx = tsubst (ctx, args, complain, in_decl);
+  if (dependent_scope_p (ctx))
+    return build_qualified_name (NULL_TREE, ctx, DECL_NAME (t),
+				 /*template_p=*/false);
+
+  return NULL_TREE;
+}
+
 /* Like tsubst, but deals with expressions.  This function just replaces
    template parms; to finish processing the resultant expression, use
    tsubst_copy_and_build or tsubst_expr.  */
@@ -16371,6 +16408,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	if (args == NULL_TREE)
 	  return scalar_constant_value (t);
 
+	if (tree ref = maybe_dependent_member_ref (t, args, complain, in_decl))
+	  return ref;
+
 	/* Unfortunately, we cannot just call lookup_name here.
 	   Consider:
 
@@ -16421,6 +16461,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       return t;
 
     case VAR_DECL:
+      if (tree ref = maybe_dependent_member_ref (t, args, complain, in_decl))
+	return ref;
+      gcc_fallthrough();
     case FUNCTION_DECL:
       if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t))
 	r = tsubst (t, args, complain, in_decl);
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C
new file mode 100644
index 00000000000..f2c3ffda85a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C
@@ -0,0 +1,44 @@
+// PR c++/96199
+// { dg-do compile { target c++2a } }
+
+template<int> struct A1 { };
+template<int&> struct A2 { };
+template<class> struct A3 { };
+
+int i;
+template<typename V, int> struct B {
+  enum E { X };
+  B(A1<X>, V) { }
+
+  constexpr static V& ir = i;
+  B(A2<ir>, V) { }
+
+  B(A3<E>, V);
+};
+
+// template<class T, int I> B(A1<B<T,I>::X>,T) -> B<T,I>;
+// template<class T, int I> B(A2<B<T,I>::ir>,T) -> B<T,I>;
+// template<class T, int I> B(A3<typename B<T,I>::E>,T) -> B<T,I>;
+
+template <typename T> using U = B<T, 1>;
+
+// template<class T> B(A1<B<T,1>::X>,T) -> B<T,1>;
+// template<class T> B(A2<B<T,1>::ir>,T) -> B<T,1>;
+// template<class T> B(A3<typename B<T,1>::E>,T) -> B<T,1>;
+
+int j;
+template <> struct B<int, 1> {
+  using V = int;
+
+  enum E { X = 1 };
+  B(A1<X>, V) { }
+
+  constexpr static V& ir = j;
+  B(A2<ir>, V) { }
+
+  B(A3<E>, V);
+};
+
+U u1 { A1<1>(), 42 };
+U u2 { A2<j>(), 42 };
+U u3 { A3<U<int>::E>(), 42 };

base-commit: 3c04bd60e56da399a441f73ebb687b5039b9cf3f
-- 
2.18.1



More information about the Gcc-patches mailing list