On 9/29/20 10:26 AM, Patrick Palka wrote:
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 it caches this result in 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, it's not constrained.
This patch fixes this issue by moving up the call to
associate_classtype_constraints so that we set constraints before we
begin processing its bases.
Tested on x86_64-pc-linux-gnu, and also on the cmcstlv2 and range-v3
libraries. Does this look OK to commit?
OK for trunk and 10.
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, before we process bases.
* 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 | 8 ++++----
gcc/cp/pt.c | 7 ++++---
gcc/testsuite/g++.dg/cpp2a/concepts-class2.C | 11 +++++++++++
3 files changed, 19 insertions(+), 7 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-class2.C
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 8905833fbd6..b44bdf21e1d 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -23978,10 +23978,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)
{
@@ -24749,6 +24745,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:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 199fe658f71..96ad2025893 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8126,9 +8126,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
index 00000000000..0ed9eb0a386
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-class2.C
@@ -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;