[C++] More fun with typedef to dependent alias.

Message ID 20191127214336.6632-1-jason@redhat.com
State New
Headers show
Series
  • [C++] More fun with typedef to dependent alias.
Related show

Commit Message

Jason Merrill Nov. 27, 2019, 9:43 p.m.
rsandifo's patch for 92206 demonstrated a problem with the existing checking
for alias template specializations: they were returning false for a typedef
to an alias template specialization.  Which is sometimes what the caller
wants, and sometimes not: Sometimes we're interested in whether the type was
written as an alias template-id, and sometimes whether it represents one.

The testcase illustrates a case that remained wrong with the earlier patch:
if the typedef is itself an alias template specialization, we can't strip an
underlying dependent alias.

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

	* pt.c (dependent_alias_template_spec_p)
	(alias_template_specialization_p): Add transparent_typedefs
	parameter.
	(iterative_hash_template_arg, any_template_parm_r)
	(primary_template_specialization_p, tsubst, dependent_type_p_r):
	Adjust.
	* decl.c (check_elaborated_type_specifier): Adjust.
	* error.c (dump_template_bindings, dump_aggr_type): Adjust.
---
 gcc/cp/cp-tree.h                              |  5 +-
 gcc/cp/decl.c                                 |  2 +-
 gcc/cp/error.c                                |  6 +-
 gcc/cp/pt.c                                   | 70 ++++++++++++-------
 gcc/cp/tree.c                                 | 11 ++-
 gcc/cp/typeck.c                               | 11 +--
 .../g++.dg/cpp0x/alias-decl-pr92206-4.C       | 11 +++
 7 files changed, 77 insertions(+), 39 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-pr92206-4.C


base-commit: 5f71c0b41967378074ac4f60bf3cc6ff5447a145
-- 
2.18.1

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4b4bc245d81..d8e12e99ba3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6938,8 +6938,9 @@  extern tree instantiate_non_dependent_expr_internal (tree, tsubst_flags_t);
 extern tree instantiate_non_dependent_or_null   (tree);
 extern bool variable_template_specialization_p  (tree);
 extern bool alias_type_or_template_p            (tree);
-extern bool alias_template_specialization_p     (const_tree);
-extern bool dependent_alias_template_spec_p     (const_tree);
+enum { nt_opaque = false, nt_transparent = true };
+extern tree alias_template_specialization_p     (const_tree, bool);
+extern tree dependent_alias_template_spec_p     (const_tree, bool);
 extern bool template_parm_object_p		(const_tree);
 extern bool explicit_class_specialization_p     (tree);
 extern bool push_tinst_level                    (tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 26120720f07..7d5bc914d2d 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14548,7 +14548,7 @@  check_elaborated_type_specifier (enum tag_types tag_code,
 	   && !DECL_SELF_REFERENCE_P (decl)
 	   && tag_code != typename_type)
     {
-      if (alias_template_specialization_p (type))
+      if (alias_template_specialization_p (type, nt_opaque))
 	error ("using alias template specialization %qT after %qs",
 	       type, tag_name (tag_code));
       else
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 4261d3c4cc9..a15230a1f01 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -421,7 +421,7 @@  dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
 static void
 dump_alias_template_specialization (cxx_pretty_printer *pp, tree t, int flags)
 {
-  gcc_assert (alias_template_specialization_p (t));
+  gcc_assert (alias_template_specialization_p (t, nt_opaque));
 
   tree decl = TYPE_NAME (t);
   if (!(flags & TFF_UNQUALIFIED_NAME))
@@ -454,7 +454,7 @@  dump_type (cxx_pretty_printer *pp, tree t, int flags)
 				    ? STF_USER_VISIBLE : 0);
 	  t = strip_typedefs (t, NULL, stf_flags);
 	}
-      else if (alias_template_specialization_p (t))
+      else if (alias_template_specialization_p (t, nt_opaque))
 	{
 	  dump_alias_template_specialization (pp, t, flags);
 	  return;
@@ -711,7 +711,7 @@  dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
       typdef = (!DECL_ARTIFICIAL (name)
 		/* An alias specialization is not considered to be a
 		   typedef.  */
-		&& !alias_template_specialization_p (t));
+		&& !alias_template_specialization_p (t, nt_opaque));
 
       if ((typdef
 	   && ((flags & TFF_CHASE_TYPEDEF)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3eed27bb426..244eb7d4ff3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1882,7 +1882,7 @@  iterative_hash_template_arg (tree arg, hashval_t val)
   switch (tclass)
     {
     case tcc_type:
-      if (alias_template_specialization_p (arg))
+      if (tree ats = alias_template_specialization_p (arg, nt_transparent))
 	{
 	  // We want an alias specialization that survived strip_typedefs
 	  // to hash differently from its TYPE_CANONICAL, to avoid hash
@@ -1891,7 +1891,7 @@  iterative_hash_template_arg (tree arg, hashval_t val)
 	  // left alone, or untouched specializations because
 	  // coerce_template_parms returns the unconverted template
 	  // arguments if it sees incomplete argument packs.
-	  tree ti = TYPE_ALIAS_TEMPLATE_INFO (arg);
+	  tree ti = TYPE_ALIAS_TEMPLATE_INFO (ats);
 	  return hash_tmpl_and_args (TI_TEMPLATE (ti), TI_ARGS (ti));
 	}
       if (TYPE_CANONICAL (arg))
@@ -3575,7 +3575,7 @@  primary_template_specialization_p (const_tree t)
     return (CLASSTYPE_TEMPLATE_INFO (t)
 	    && CLASSTYPE_USE_TEMPLATE (t)
 	    && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
-  else if (alias_template_specialization_p (t))
+  else if (alias_template_specialization_p (t, nt_transparent))
     return true;
   return false;
 }
@@ -6298,18 +6298,30 @@  alias_type_or_template_p (tree t)
 	  || DECL_ALIAS_TEMPLATE_P (t));
 }
 
-/* Return TRUE iff T is a specialization of an alias template.  */
+/* If T is a specialization of an alias template, return it; otherwise return
+   NULL_TREE.  If TRANSPARENT_TYPEDEFS is true, look through other aliases.  */
 
-bool
-alias_template_specialization_p (const_tree t)
+tree
+alias_template_specialization_p (const_tree t,
+				 bool transparent_typedefs)
 {
+  if (!TYPE_P (t))
+    return NULL_TREE;
+
   /* It's an alias template specialization if it's an alias and its
      TYPE_NAME is a specialization of a primary template.  */
-  if (TYPE_ALIAS_P (t))
-    if (tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t))
-      return PRIMARY_TEMPLATE_P (TI_TEMPLATE (tinfo));
+  if (typedef_variant_p (t))
+    {
+      if (tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t))
+	if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (tinfo)))
+	  return CONST_CAST_TREE (t);
+      if (transparent_typedefs)
+	return alias_template_specialization_p (DECL_ORIGINAL_TYPE
+						(TYPE_NAME (t)),
+						transparent_typedefs);
+    }
 
-  return false;
+  return NULL_TREE;
 }
 
 /* An alias template is complex from a SFINAE perspective if a template-id
@@ -6354,24 +6366,30 @@  complex_alias_template_p (const_tree tmpl)
   return false;
 }
 
-/* Return TRUE iff T is a specialization of a complex alias template with
-   dependent template-arguments.  */
+/* If T is a specialization of a complex alias template with dependent
+   template-arguments, return it; otherwise return NULL_TREE.  If T is a
+   typedef to such a specialization, return the specialization.  */
 
-bool
-dependent_alias_template_spec_p (const_tree t)
+tree
+dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs)
 {
-  if (!alias_template_specialization_p (t))
-    return false;
+  if (!TYPE_P (t) || !typedef_variant_p (t))
+    return NULL_TREE;
 
   tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t);
-  if (!TEMPLATE_DECL_COMPLEX_ALIAS_P (TI_TEMPLATE (tinfo)))
-    return false;
+  if (tinfo
+      && TEMPLATE_DECL_COMPLEX_ALIAS_P (TI_TEMPLATE (tinfo))
+      && (any_dependent_template_arguments_p
+	  (INNERMOST_TEMPLATE_ARGS (TI_ARGS (tinfo)))))
+    return CONST_CAST_TREE (t);
 
-  tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (tinfo));
-  if (!any_dependent_template_arguments_p (args))
-    return false;
+  if (transparent_typedefs)
+    {
+      tree utype = DECL_ORIGINAL_TYPE (TYPE_NAME (t));
+      return dependent_alias_template_spec_p (utype, transparent_typedefs);
+    }
 
-  return true;
+  return NULL_TREE;
 }
 
 /* Return the number of innermost template parameters in TMPL.  */
@@ -10384,9 +10402,9 @@  any_template_parm_r (tree t, void *data)
     case UNION_TYPE:
     case ENUMERAL_TYPE:
       /* Search for template parameters in type aliases.  */
-      if (alias_template_specialization_p (t))
+      if (tree ats = alias_template_specialization_p (t, nt_opaque))
 	{
-	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t);
+	  tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (ats);
 	  WALK_SUBTREE (TI_ARGS (tinfo));
         }
       break;
@@ -14896,7 +14914,7 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     {
       tree decl = TYPE_NAME (t);
 
-      if (alias_template_specialization_p (t))
+      if (alias_template_specialization_p (t, nt_opaque))
 	{
 	  /* DECL represents an alias template and we want to
 	     instantiate it.  */
@@ -25815,7 +25833,7 @@  dependent_type_p_r (tree type)
 
   /* An alias template specialization can be dependent even if the
      resulting type is not.  */
-  if (dependent_alias_template_spec_p (type))
+  if (dependent_alias_template_spec_p (type, nt_transparent))
     return true;
 
   /* -- a cv-qualified type where the cv-unqualified type is
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d125d60b270..f500ee61442 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1489,7 +1489,7 @@  strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
     return t;
 
   if (!(flags & STF_STRIP_DEPENDENT)
-      && dependent_alias_template_spec_p (t))
+      && dependent_alias_template_spec_p (t, nt_opaque))
     /* DR 1558: However, if the template-id is dependent, subsequent
        template argument substitution still applies to the template-id.  */
     return t;
@@ -1673,14 +1673,19 @@  strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
 	  if ((flags & STF_USER_VISIBLE)
 	      && !user_facing_original_type_p (t))
 	    return t;
+	  /* If T is a non-template alias or typedef, we can assume that
+	     instantiating its definition will hit any substitution failure,
+	     so we don't need to retain it here as well.  */
+	  if (!alias_template_specialization_p (t, nt_opaque))
+	    flags |= STF_STRIP_DEPENDENT;
 	  result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
-				   remove_attributes,
-				   flags | STF_STRIP_DEPENDENT);
+				   remove_attributes, flags);
 	}
       else
 	result = TYPE_MAIN_VARIANT (t);
     }
   gcc_assert (!typedef_variant_p (result)
+	      || dependent_alias_template_spec_p (result, nt_opaque)
 	      || ((flags & STF_USER_VISIBLE)
 		  && !user_facing_original_type_p (result)));
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 98c428db52e..a9b87ea8caf 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1463,10 +1463,13 @@  structural_comptypes (tree t1, tree t2, int strict)
      substitute into the specialization arguments at instantiation
      time.  And aliases can't be equivalent without being ==, so
      we don't need to look any deeper.  */
-  if (comparing_specializations
-      && (dependent_alias_template_spec_p (t1)
-	  || dependent_alias_template_spec_p (t2)))
-    return false;
+  if (comparing_specializations)
+    {
+      tree dep1 = dependent_alias_template_spec_p (t1, nt_transparent);
+      tree dep2 = dependent_alias_template_spec_p (t2, nt_transparent);
+      if ((dep1 || dep2) && dep1 != dep2)
+	return false;
+    }
 
   /* If we get here, we know that from a target independent POV the
      types are the same.  Make sure the target attributes are also
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-pr92206-4.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-pr92206-4.C
new file mode 100644
index 00000000000..4c86d4567ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-pr92206-4.C
@@ -0,0 +1,11 @@ 
+// { dg-do compile { target c++11 } }
+
+template <typename T> struct A { };
+template <typename T, int = sizeof(typename T::type)> using AA = A<T>; // { dg-error  "char" }
+template <typename T> using AAA = AA<T>;
+
+template <class T> struct C { };
+template <class T> struct B {
+  C<AAA<T>> a;
+};
+B<char> b;			// { dg-message "" }