To bring the implementation in line with changes going into the concepts
draft, use syntactical equivalence in place of logical equivalence when
matching declarations.
2014-06-27 Braden Obrzut <ad...@maniacsvault.net>
* gcc/cp/constraint.c (equivalent_constraints): Compare the trees
directly
instead of logically to bring implementation in line with standard
changes.
* gcc/cp/tree.c (cp_tree_equal): Handle comparison of constraints.
* gcc/testsuite/g++.dg/concepts/equiv.C: New test.
* gcc/testsuite/g++.dg/concepts/equiv-err.C: New test.
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 01e83ba..5a3cf4d 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1199,7 +1199,7 @@ equivalent_constraints (tree a, tree b)
if (a == b)
return true;
else
- return subsumes (a, b) && subsumes (b, a);
+ return cp_tree_equal (a, b);
}
// Returns true if the template declarations A and B have equivalent
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 5d88806..f1d8545 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2775,6 +2775,10 @@ cp_tree_equal (tree t1, tree t2)
&& same_type_p (TREE_TYPE (TEMPLATE_PARM_DECL (t1)),
TREE_TYPE (TEMPLATE_PARM_DECL (t2))));
+ case CONSTRAINT_INFO:
+ return cp_tree_equal (CI_SPELLING (t1), CI_SPELLING (t2));
+
+ case REQUIRES_EXPR:
case TEMPLATE_ID_EXPR:
return (cp_tree_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0))
&& cp_tree_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1)));
diff --git a/gcc/testsuite/g++.dg/concepts/equiv-err.C b/gcc/testsuite/g++.dg/concepts/equiv-err.C
new file mode 100644
index 0000000..7dc8b10
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/equiv-err.C
@@ -0,0 +1,108 @@
+// { dg-options "-std=c++1y" }
+
+// Test case for determining syntactical equivalence in constraints.
+// Tests for compile time errors
+
+#include <cassert>
+
+int called;
+
+template<typename T>
+ concept bool C() { return __is_class(T); }
+
+template<typename T>
+ concept bool D() { return requires (T a) { { a + a } -> T }; }
+
+template<typename T>
+ concept bool CD()
+ {
+ return __is_class(T)
+ && requires (T a) { { a + a } -> T };
+ }
+
+template<typename T>
+ requires CD<T>()
+ void f1(T);
+
+struct S1
+{
+ void f2(CD); // { dg-error "candidate" }
+
+ static void f3(CD, CD); // { dg-error "candidate" }
+};
+
+struct S2
+{
+ S2 operator+ (const S2 &other) { return S2(); }
+};
+
+template<typename T>
+struct S3
+{
+ void f4(T);
+
+ static void f5(T);
+};
+
+template<CD T>
+struct S3<T>
+{
+ void f4(T);
+
+ static void f5(T);
+};
+
+int main()
+{
+ S1 s;
+ S2 v;
+ S3<S2> v2;
+ S3<int> v3;
+
+ f1(v); assert (called == 1);
+
+ s.f2(v); assert (called == 1);
+
+ S1::f3(v, v); assert (called == 1);
+
+ v2.f4(v); assert (called == 1);
+
+ S3<S2>::f5(v); assert (called == 1);
+ return 0;
+}
+
+// This template has the same atomic constraints in the same order, but is
+// not considered equivalent, so this should never get called.
+template<typename T>
+ requires C<T>() && D<T>()
+ void f1(T)
+ {
+ called = 3;
+ }
+
+template<typename T>
+ requires C<T>() && D<T>()
+ void S1::f2(T) // { dg-error "match any" }
+ {
+ called = 1;
+ }
+
+template<C T>
+ requires D<T>()
+ void S1::f3(T, T) // { dg-error "match any" }
+ {
+ called = 1;
+ }
+
+template<D T>
+ requires C<T>()
+ void S3<T>::f4(T)
+ {
+ called = 1;
+ }
+
+template<CD T>
+ void S3<T>::f5(T)
+ {
+ called = 1;
+ }
diff --git a/gcc/testsuite/g++.dg/concepts/equiv.C b/gcc/testsuite/g++.dg/concepts/equiv.C
new file mode 100644
index 0000000..e97d4db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/equiv.C
@@ -0,0 +1,144 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+// Test case for determining syntactical equivalence in constraints.
+
+#include <cassert>
+
+int called;
+
+template<typename T>
+ concept bool C() { return __is_class(T); }
+
+template<typename T>
+ concept bool D() { return requires (T a) { { a + a } -> T }; }
+
+template<typename T>
+ concept bool CD()
+ {
+ return __is_class(T)
+ && requires (T a) { { a + a } -> T };
+ }
+
+template<typename T>
+ requires CD<T>()
+ void f1(T);
+void f1(auto) { called = 2; }
+
+struct S1
+{
+ void f2(C);
+ void f2(CD);
+
+ template<typename T>
+ static void f3(T, T);
+ static void f3(CD, CD);
+};
+
+struct S2
+{
+ S2 operator+ (const S2 &other) { return S2(); }
+};
+
+template<typename T>
+struct S3
+{
+ void f4(T);
+
+ static void f5(T);
+};
+
+template<CD T>
+struct S3<T>
+{
+ void f4(T);
+
+ static void f5(T);
+};
+
+int main()
+{
+ S1 s;
+ S2 v;
+ S3<S2> v2;
+ S3<int> v3;
+
+ f1(v); assert (called == 1);
+ f1(123); assert (called == 2);
+
+ s.f2(v); assert (called == 1);
+ s.f2(s); assert (called == 2);
+
+ S1::f3(12, 34); assert (called == 2);
+ S1::f3(v, v); assert (called == 1);
+
+ v2.f4(v); assert (called == 1);
+ v3.f4(123); assert (called == 2);
+
+ S3<S2>::f5(v); assert (called == 1);
+ S3<int>::f5(123); assert (called == 2);
+ return 0;
+}
+
+// This template has the same atomic constraints in the same order, but is
+// not considered equivalent, so this should never get called.
+template<typename T>
+ requires C<T>() && D<T>()
+ void f1(T)
+ {
+ called = 3;
+ }
+
+void f1(CD)
+{
+ called = 1;
+}
+
+template<C T>
+ void S1::f2(T)
+ {
+ called = 2;
+ }
+
+void S1::f2(CD)
+{
+ called = 1;
+}
+
+template<typename T>
+ requires CD<T>()
+ void S1::f3(T, T)
+ {
+ called = 1;
+ }
+
+template<typename T>
+ void S1::f3(T, T)
+ {
+ called = 2;
+ }
+
+template<CD T>
+ void S3<T>::f4(T)
+ {
+ called = 1;
+ }
+
+template<typename T>
+ void S3<T>::f4(T)
+ {
+ called = 2;
+ }
+
+template<typename T>
+ requires CD<T>()
+ void S3<T>::f5(T)
+ {
+ called = 1;
+ }
+
+template<typename T>
+ void S3<T>::f5(T)
+ {
+ called = 2;
+ }