aaron.ballman updated this revision to Diff 515021.
aaron.ballman added a comment.

Added an additional test case (assignment of a null pointer constant to an 
atomic function pointer) that was mentioned in a comment on the original bug 
report.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D148730/new/

https://reviews.llvm.org/D148730

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/AST/Expr.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/test/Sema/atomic-expr.c

Index: clang/test/Sema/atomic-expr.c
===================================================================
--- clang/test/Sema/atomic-expr.c
+++ clang/test/Sema/atomic-expr.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 %s -verify -fsyntax-only
-// RUN: %clang_cc1 %s -verify=off -fsyntax-only -Wno-atomic-access
-// off-no-diagnostics
+// RUN: %clang_cc1 %s -verify=expected,access -fsyntax-only
+// RUN: %clang_cc1 %s -std=c2x -verify=expected,access -fsyntax-only
+// RUN: %clang_cc1 %s -verify -fsyntax-only -Wno-atomic-access
 
 _Atomic(unsigned int) data1;
 int _Atomic data2;
@@ -82,26 +82,26 @@
 void func_16(void) {
   // LHS member access.
   _Atomic struct { int val; } x, *xp;
-  x.val = 12;   // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  xp->val = 12; // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  x.val = 12;   // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  xp->val = 12; // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
 
   _Atomic union {
     int ival;
     float fval;
   } y, *yp;
-  y.ival = 12;     // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  yp->fval = 1.2f; // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  y.ival = 12;     // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  yp->fval = 1.2f; // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
 
   // RHS member access.
-  int xval = x.val; // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  xval = xp->val;   // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  int yval = y.ival; // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  yval = yp->ival;   // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  int xval = x.val; // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  xval = xp->val;   // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  int yval = y.ival; // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  yval = yp->ival;   // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
 
   // Using the type specifier instead of the type qualifier.
   _Atomic(struct { int val; }) z;
-  z.val = 12;       // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
-  int zval = z.val; // expected-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  z.val = 12;       // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
+  int zval = z.val; // access-error {{accessing a member of an atomic structure or union is undefined behavior}}
 
   // Don't diagnose in an unevaluated context, however.
   (void)sizeof(x.val);
@@ -109,3 +109,97 @@
   (void)sizeof(y.ival);
   (void)sizeof(yp->ival);
 }
+
+// Ensure that we correctly implement assignment constraints from C2x 6.5.16.1.
+void func_17(void) {
+  // The left operand has atomic ... arithmetic type, and the right operand has
+  // arithmetic type;
+  _Atomic int i = 0;
+  _Atomic float f = 0.0f;
+
+  // the left operand has an atomic ... version of a structure or union type
+  // compatible with the type of the right operand;
+  struct S { int i; } non_atomic_s;
+  _Atomic struct S s = non_atomic_s;
+
+  union U { int i; float f; } non_atomic_u;
+  _Atomic union U u = non_atomic_u;
+
+  // the left operand has atomic ... pointer type, and (considering the type
+  // the left operand would have after lvalue conversion) both operands are
+  // pointers to qualified or unqualified versions of compatible types, and the
+  // type pointed to by the left operand has all the qualifiers of the type
+  // pointed to by the right operand;
+  const int *cip = 0;
+  volatile const int *vcip = 0;
+  const int * const cicp = 0;
+  _Atomic(const int *) acip = cip;
+  _Atomic(const int *) bad_acip = vcip; // expected-warning {{initializing '_Atomic(const int *)' with an expression of type 'const volatile int *' discards qualifiers}}
+  _Atomic(const int *) acip2 = cicp;
+  _Atomic(int *) aip = &i; // expected-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \
+
+  // the left operand has atomic ... pointer type, and (considering the type
+  // the left operand would have after lvalue conversion) one operand is a
+  // pointer to an object type, and the other is a pointer to a qualified or
+  // unqualified version of void, and the type pointed to by the left operand
+  // has all the qualifiers of the type pointed to by the right operand;
+  const void *cvp = 0;
+  _Atomic(const int *) acip3 = cvp;
+  _Atomic(const void *) acvip = cip;
+  _Atomic(const int *) acip4 = vcip;   // expected-warning {{initializing '_Atomic(const int *)' with an expression of type 'const volatile int *' discards qualifiers}}
+  _Atomic(const void *) acvip2 = vcip; // expected-warning {{initializing '_Atomic(const void *)' with an expression of type 'const volatile int *' discards qualifiers}}
+  _Atomic(const int *) acip5 = cicp;
+  _Atomic(const void *) acvip3 = cicp;
+
+#if __STDC_VERSION__ >= 202000L
+  // the left operand has an atomic ... version of the nullptr_t type and the
+  // right operand is a null pointer constant or its type is nullptr_t
+  typedef typeof(nullptr) nullptr_t;
+  nullptr_t n;
+  _Atomic nullptr_t cn2 = n;
+  _Atomic nullptr_t cn3 = nullptr;
+#endif // __STDC_VERSION__ >= 202000L
+
+  // the left operand is an atomic ... pointer, and the right operand is a null
+  // pointer constant or its type is nullptr_t;
+  _Atomic(int *) aip2 = 0;
+#if __STDC_VERSION__ >= 202000L
+  _Atomic(int *) ip2 = n;
+  _Atomic(int *) ip3 = nullptr;
+  _Atomic(const int *) ip4 = nullptr;
+#endif // __STDC_VERSION__ >= 202000L
+}
+
+// Ensure that the assignment constraints also work at file scope.
+_Atomic int ai = 0;
+_Atomic float af = 0.0f;
+_Atomic(int *) aip1 = 0;
+
+struct S { int a; } non_atomic_s;
+_Atomic struct S as = non_atomic_s; // expected-error {{initializer element is not a compile-time constant}}
+
+const int *cip = 0;
+_Atomic(const int *) acip1 = cip; // expected-error {{initializer element is not a compile-time constant}}
+
+const void *cvp = 0;
+_Atomic(const int *) acip2 = cvp; // expected-error {{initializer element is not a compile-time constant}}
+
+#if __STDC_VERSION__ >= 202000L
+  // the left operand has an atomic ... version of the nullptr_t type and the
+  // right operand is a null pointer constant or its type is nullptr_t
+  typedef typeof(nullptr) nullptr_t;
+  nullptr_t n;
+  _Atomic nullptr_t cn2 = n; // expected-error {{initializer element is not a compile-time constant}}
+  _Atomic(int *) aip2 = nullptr;
+#endif // __STDC_VERSION__ >= 202000L
+
+// FIXME: &ai is an address constant, so this should be accepted as an
+// initializer, but the bit-cast inserted due to the pointer conversion is
+// tripping up the test for whether the initializer is a constant expression.
+// The warning is correct but the error is not.
+_Atomic(int *) aip3 = &ai; // expected-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \
+                              expected-error {{initializer element is not a compile-time constant}}
+
+// Test the behavior when converting the null pointer constant to an atomic
+// function pointer.
+_Atomic(int (*)(char)) afp = 0;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -10290,10 +10290,15 @@
       return Incompatible;
   }
 
+  // The constraints are expressed in terms of the atomic, qualified, or
+  // unqualified type of the LHS.
+  QualType LHSTypeAfterConversion = LHSType.getAtomicUnqualifiedType();
+
   // C99 6.5.16.1p1: the left operand is a pointer and the right is
   // a null pointer constant.
-  if ((LHSType->isPointerType() || LHSType->isObjCObjectPointerType() ||
-       LHSType->isBlockPointerType()) &&
+  if ((LHSTypeAfterConversion->isPointerType() ||
+       LHSTypeAfterConversion->isObjCObjectPointerType() ||
+       LHSTypeAfterConversion->isBlockPointerType()) &&
       RHS.get()->isNullPointerConstant(Context,
                                        Expr::NPC_ValueDependentIsNull)) {
     if (Diagnose || ConvertRHS) {
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -3427,6 +3427,7 @@
         CE->getCastKind() == CK_ConstructorConversion ||
         CE->getCastKind() == CK_NonAtomicToAtomic ||
         CE->getCastKind() == CK_AtomicToNonAtomic ||
+        CE->getCastKind() == CK_NullToPointer ||
         CE->getCastKind() == CK_IntToOCLSampler)
       return CE->getSubExpr()->isConstantInitializer(Ctx, false, Culprit);
 
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -110,6 +110,8 @@
 ------------------
 - Support for outputs from asm goto statements along indirect edges has been
   added. (`#53562 <https://github.com/llvm/llvm-project/issues/53562>`_)
+- Fixed a bug that prevented initialization of an ``_Atomic``-qualified pointer
+  from a null pointer constant.
 
 C2x Feature Support
 ^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to