[clang] 2e275e2 - [C11] Allow initialization of an atomic-qualified pointer from a null pointer constant
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 20 05:02:49 PDT 2023
Author: Aaron Ballman
Date: 2023-04-20T08:02:40-04:00
New Revision: 2e275e24355cb224981f9beb2b026a3169fc7232
URL: https://github.com/llvm/llvm-project/commit/2e275e24355cb224981f9beb2b026a3169fc7232
DIFF: https://github.com/llvm/llvm-project/commit/2e275e24355cb224981f9beb2b026a3169fc7232.diff
LOG: [C11] Allow initialization of an atomic-qualified pointer from a null pointer constant
The relevant language rule from C11 is 6.5.16.1p1: "the left operand is
an atomic, qualified, or unqualified pointer, and the right is a null
pointer constant; or". We correctly handled qualified or unqualified
pointer types, but failed to handle atomic-qualified pointer types. Now
we look through the atomic qualification before testing the constraint
requirements.
Fixes https://github.com/llvm/llvm-project/issues/49563
Differential Revision: https://reviews.llvm.org/D148730
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/AST/Expr.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/Sema/atomic-expr.c
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 05189bfb6abad..1babd4d459f9b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -110,6 +110,8 @@ C Language Changes
------------------
- 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
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index eca78060b7e7b..756acc37d569e 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3427,6 +3427,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
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);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 87ea08748a9f2..fd64a79d164bb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10288,10 +10288,15 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
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) {
diff --git a/clang/test/Sema/atomic-expr.c b/clang/test/Sema/atomic-expr.c
index 6c6823c848722..5cb9df411044e 100644
--- a/clang/test/Sema/atomic-expr.c
+++ b/clang/test/Sema/atomic-expr.c
@@ -1,6 +1,7 @@
-// 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 -std=c2x -pedantic -verify=expected,access -fsyntax-only
+// RUN: %clang_cc1 %s -verify -fsyntax-only -Wno-atomic-access
_Atomic(unsigned int) data1;
int _Atomic data2;
@@ -82,26 +83,26 @@ void func_15(void) {
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 +110,98 @@ void func_16(void) {
(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 = (void *)0;
More information about the cfe-commits
mailing list