c++/71675 - __atomic_compare_exchange_n returns wrong type for
typed enum

__atomic_compare_exchange_n is documented to return bool but when
its operands are of one of the character types or derived from
it (such as a class enum in C++) it converts the bool result to
that type.  The attached patch corrects that to prevent this
conversion, analogously to what's already being done for
__sync_bool_compare_and_swap.

Martin
PR c++/71675 - __atomic_compare_exchange_n returns wrong type for typed enum

gcc/c-family/ChangeLog:
2016-06-28  Martin Sebor  <mse...@redhat.com>

	PR c++/71675
	* c-common.c (resolve_overloaded_builtin): Avoid converting
	__atomic_compare_exchange_n return type to that of what its
	first argument points to.

gcc/testsuite/ChangeLog:
2016-06-28  Martin Sebor  <mse...@redhat.com>

	PR c++/71675
	* g++.dg/ext/atomic-3.C: New test.
	* gcc.dg/atomic/pr71675.c: New test.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 524fbc5..e4e6d5f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -11503,7 +11503,8 @@ resolve_overloaded_builtin (location_t loc, tree function,
 	  return result;
 	if (orig_code != BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_N
 	    && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N
-	    && orig_code != BUILT_IN_ATOMIC_STORE_N)
+	    && orig_code != BUILT_IN_ATOMIC_STORE_N
+	    && orig_code != BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N)
 	  result = sync_resolve_return (first_param, result, orig_format);
 
 	if (fetch_op)
diff --git a/gcc/testsuite/g++.dg/ext/atomic-3.C b/gcc/testsuite/g++.dg/ext/atomic-3.C
new file mode 100644
index 0000000..f9e102e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/atomic-3.C
@@ -0,0 +1,37 @@
+// PR c++/71675 - __atomic_compare_exchange_n returns wrong type for typed enum
+// { dg-do compile { target c++11 } }
+
+template <class T>
+void sink (T);
+
+bool sink (bool);
+
+template <class T>
+bool test ()
+{
+  enum class E: T { };
+  static E e;
+
+  return sink (__atomic_compare_exchange_n (&e, &e, e, false, 0, 0));
+}
+
+void tests ()
+{
+  // __atomic_compare_exchange_n would fail to return bool when
+  //   its arguments were one of the three character types.
+  test<char>();
+  test<signed char>();
+  test<unsigned char>();
+
+  test<short>();
+  test<unsigned short>();
+
+  test<int>();
+  test<unsigned int>();
+
+  test<long>();
+  test<unsigned long>();
+
+  test<long long>();
+  test<unsigned long long>();
+}
diff --git a/gcc/testsuite/gcc.dg/atomic/pr71675.c b/gcc/testsuite/gcc.dg/atomic/pr71675.c
new file mode 100644
index 0000000..0e344ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/atomic/pr71675.c
@@ -0,0 +1,32 @@
+/* PR c++/71675 - __atomic_compare_exchange_n returns wrong type for typed enum
+ */
+/* { dg-do compile { target c11 } } */
+
+#define Test(T)								\
+  do {									\
+    static T x;								\
+    int r [_Generic (__atomic_compare_exchange_n (&x, &x, x, 0, 0, 0),	\
+		     _Bool: 1, default: -1)];				\
+    (void)&r;								\
+  } while (0)
+
+void f (void)
+{
+  /* __atomic_compare_exchange_n would fail to return _Bool when
+     its arguments were one of the three character types. */
+  Test (char);
+  Test (signed char);
+  Test (unsigned char);
+
+  Test (int);
+  Test (unsigned int);
+
+  Test (long);
+  Test (unsigned long);
+
+  Test (long long);
+  Test (unsigned long long);
+
+  typedef enum E { e } E;
+  Test (E);
+}

Reply via email to