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 `_) +- Fixed a bug that prevented initialization of an ``_Atomic``-qualified pointer + from a null pointer constant. C2x Feature Support ^^^^^^^^^^^^^^^^^^^ 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/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/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,93 @@ (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}}