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;
+  }

Reply via email to