c++: Set the constraints of a class type sooner [PR96229]
authorPatrick Palka <ppalka@redhat.com>
Thu, 8 Oct 2020 04:05:36 +0000 (00:05 -0400)
committerPatrick Palka <ppalka@redhat.com>
Thu, 8 Oct 2020 04:05:36 +0000 (00:05 -0400)
In the testcase below, during processing (at parse time) of Y's base
class X<Y>, convert_template_argument calls is_compatible_template_arg
to check if the template argument Y is no more constrained than the
parameter P.  But at this point we haven't yet set Y's constraints, so
get_normalized_constraints_from_decl yields NULL_TREE as the normal form
and caches this result into the normalized_map.

We set Y's constraints later in cp_parser_class_specifier_1 but the
stale normal form in the normalized_map remains.  This ultimately causes
us to miss the constraint failure for Y<Z> because according to the
cached normal form, Y is not constrained.

This patch fixes this issue by moving up the call to
associate_classtype_constraints so that we set constraints before we
start processing a class's bases.

gcc/cp/ChangeLog:

PR c++/96229
* parser.c (cp_parser_class_specifier_1): Move call to
associate_classtype_constraints from here to ...
(cp_parser_class_head): ... here.
* pt.c (is_compatible_template_arg): Correct documentation to
say "argument is _no_ more constrained than the parameter".

gcc/testsuite/ChangeLog:

PR c++/96229
* g++.dg/cpp2a/concepts-class2.C: New test.

gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp2a/concepts-class2.C [new file with mode: 0644]

index 7a61abfa7038e3689884ec7d00e8fb2571ad1a13..592ce95b571899a89ec46050f7499c28473d2459 100644 (file)
@@ -24044,10 +24044,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
     = parser->in_unbraced_linkage_specification_p;
   parser->in_unbraced_linkage_specification_p = false;
 
-  // Associate constraints with the type.
-  if (flag_concepts)
-    type = associate_classtype_constraints (type);
-
   /* Start the class.  */
   if (nested_name_specifier_p)
     {
@@ -24815,6 +24811,10 @@ cp_parser_class_head (cp_parser* parser,
       fixup_attribute_variants (type);
     }
 
+  /* Associate constraints with the type.  */
+  if (flag_concepts)
+    type = associate_classtype_constraints (type);
+
   /* We will have entered the scope containing the class; the names of
      base classes should be looked up in that context.  For example:
 
index d9cc7766a5907aa4fb68575a545f9550cf74d3d8..fc4b9bb7c7f9810f4daab5859a9ad9a20d1f98d5 100644 (file)
@@ -8127,9 +8127,10 @@ canonicalize_expr_argument (tree arg, tsubst_flags_t complain)
   return canon;
 }
 
-// A template declaration can be substituted for a constrained
-// template template parameter only when the argument is more
-// constrained than the parameter.
+/* A template declaration can be substituted for a constrained
+   template template parameter only when the argument is no more
+   constrained than the parameter.  */
+
 static bool
 is_compatible_template_arg (tree parm, tree arg)
 {
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-class2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-class2.C
new file mode 100644 (file)
index 0000000..0ed9eb0
--- /dev/null
@@ -0,0 +1,11 @@
+// PR c++/96229
+// { dg-do compile { target c++20 } }
+
+template <class T> concept Int = requires { T{0}; };
+template <template <Int> class P> struct X        { };
+template <Int>                    struct Y : X<Y> { };
+                                  struct Z        { };
+                                  struct W        { int i; };
+
+Y<Z> z; // { dg-error "constraint" }
+Y<W> w;