diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -452,6 +452,32 @@ typeof(Val) OtherVal; // type is '__attribute__((address_space(1))) const _Atomic int' typeof_unqual(Val) OtherValUnqual; // type is 'int' +- Implemented `WG14 N3042 `_, + Introduce the nullptr constant. This introduces a new type ``nullptr_t``, + declared in ```` which represents the type of the null pointer named + constant, ``nullptr``. This constant is implicitly convertible to any pointer + type and represents a type-safe null value. + + Note, there are some known incompatibilities with this same feature in C++. + The following examples were discovered during implementation and are subject + to change depending on how national body comments are resolved by WG14 (C + status is based on standard requirements, not necessarily implementation + behavior): + + .. code-block:: c + + nullptr_t null_val; + (nullptr_t)nullptr; // Rejected in C, accepted in C++, Clang accepts + (void)(1 ? nullptr : 0); // Rejected in C, accepted in C++, Clang rejects + (void)(1 ? null_val : 0); // Rejected in C, accepted in C++, Clang rejects + bool b1 = nullptr; // Accepted in C, rejected in C++, Clang rejects + b1 = null_val; // Accepted in C, rejected in C++, Clang rejects + null_val = 0; // Rejected in C, accepted in C++, Clang rejects + + void func(nullptr_t); + func(0); // Rejected in C, accepted in C++, Clang rejects + + C++ Language Changes in Clang ----------------------------- - Implemented DR692, DR1395 and DR1432. Use the ``-fclang-abi-compat=15`` option diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -761,6 +761,8 @@ /// The null pointer literal (C++11 [lex.nullptr]) /// /// Introduced in C++11, the only literal of type \c nullptr_t is \c nullptr. +/// This also implements the null pointer literal in C2x (C2x 6.4.1) which is +/// intended to have the same semantics as the feature in C++. class CXXNullPtrLiteralExpr : public Expr { public: CXXNullPtrLiteralExpr(QualType Ty, SourceLocation Loc) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -65,7 +65,8 @@ SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), SuppressTemplateArgsInCXXConstructors(false), SuppressDefaultTemplateArgs(true), Bool(LO.Bool), - Nullptr(LO.CPlusPlus11), Restrict(LO.C99), Alignof(LO.CPlusPlus11), + Nullptr(LO.CPlusPlus11 || LO.C2x), NullptrTypeInNamespace(LO.CPlusPlus), + Restrict(LO.C99), Alignof(LO.CPlusPlus11), UnderscoreAlignof(LO.C11), UseVoidForZeroParams(!LO.CPlusPlus), SplitTemplateClosers(!LO.CPlusPlus11), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), @@ -196,6 +197,9 @@ /// constant. unsigned Nullptr : 1; + /// Whether 'nullptr_t' is in namespace 'std' or not. + unsigned NullptrTypeInNamespace : 1; + /// Whether we can use 'restrict' rather than '__restrict'. unsigned Restrict : 1; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2223,7 +2223,8 @@ bool isObjCARCBridgableType() const; bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter - bool isNullPtrType() const; // C++11 std::nullptr_t + bool isNullPtrType() const; // C++11 std::nullptr_t or + // C2x nullptr_t bool isNothrowT() const; // C++ std::nothrow_t bool isAlignValT() const; // C++17 std::align_val_t bool isStdByteType() const; // C++17 std::byte diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -702,6 +702,11 @@ InGroup, DefaultIgnore; def warn_cxx98_compat_nullptr : Warning< "'nullptr' is incompatible with C++98">, InGroup, DefaultIgnore; +def ext_c_nullptr : Extension< + "'nullptr' is a C2x extension">, InGroup; +def warn_c17_compat_nullptr : Warning< + "'nullptr' is incompatible with C standards before C2x">, + InGroup, DefaultIgnore; def warn_wrong_clang_attr_namespace : Warning< "'__clang__' is a predefined macro name, not an attribute scope specifier; " diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8694,6 +8694,9 @@ InGroup, DefaultIgnore; def err_cast_pointer_to_non_pointer_int : Error< "pointer cannot be cast to type %0">; +def err_nullptr_cast : Error< + "cannot cast an object of type %select{'nullptr_t' to %1|%1 to 'nullptr_t'}0" +>; def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">; def err_cast_from_bfloat16 : Error<"cannot type-cast from __bf16">; def err_typecheck_expect_scalar_operand : Error< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -390,7 +390,7 @@ CXX11_KEYWORD(constexpr , 0) CXX11_KEYWORD(decltype , 0) CXX11_KEYWORD(noexcept , 0) -CXX11_KEYWORD(nullptr , 0) +CXX11_KEYWORD(nullptr , KEYC2X) CXX11_KEYWORD(static_assert , KEYMSCOMPAT|KEYC2X) CXX11_KEYWORD(thread_local , KEYC2X) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3930,7 +3930,7 @@ if (getType().isNull()) return NPCK_NotNull; - // C++11 nullptr_t is always a null pointer constant. + // C++11/C2x nullptr_t is always a null pointer constant. if (getType()->isNullPtrType()) return NPCK_CXX11_nullptr; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3084,7 +3084,7 @@ case Char32: return "char32_t"; case NullPtr: - return "std::nullptr_t"; + return Policy.NullptrTypeInNamespace ? "std::nullptr_t" : "nullptr_t"; case Overload: return ""; case BoundMember: diff --git a/clang/lib/Headers/stddef.h b/clang/lib/Headers/stddef.h --- a/clang/lib/Headers/stddef.h +++ b/clang/lib/Headers/stddef.h @@ -97,6 +97,12 @@ #undef __need_NULL #endif /* defined(__need_NULL) */ +/* FIXME: This is using the placeholder dates Clang produces for these macros + in C2x mode; switch to the correct values once they've been published. */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L +typedef typeof(nullptr) nullptr_t; +#endif /* defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L */ + #if defined(__need_STDDEF_H_misc) #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \ (defined(__cplusplus) && __cplusplus >= 201103L) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1003,7 +1003,12 @@ break; case tok::kw_nullptr: - Diag(Tok, diag::warn_cxx98_compat_nullptr); + if (getLangOpts().CPlusPlus) + Diag(Tok, diag::warn_cxx98_compat_nullptr); + else + Diag(Tok, getLangOpts().C2x ? diag::warn_c17_compat_nullptr + : diag::ext_c_nullptr); + Res = Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); break; diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2985,6 +2985,37 @@ return; } + // C2x 6.5.4p4: + // The type nullptr_t shall not be converted to any type other than void, + // bool, or a pointer type. No type other than nullptr_t shall be converted + // to nullptr_t. + if (SrcType->isNullPtrType()) { + // FIXME: 6.3.2.4p2 says that nullptr_t can be converted to itself, but + // 6.5.4p4 is a constraint check and nullptr_t is not void, bool, or a + // pointer type. We're not going to diagnose that as a constraint violation. + if (!DestType->isVoidType() && !DestType->isBooleanType() && + !DestType->isPointerType() && !DestType->isNullPtrType()) { + Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_nullptr_cast) + << /*nullptr to type*/ 0 << DestType; + SrcExpr = ExprError(); + return; + } + if (!DestType->isNullPtrType()) { + // Implicitly cast from the null pointer type to the type of the + // destination. + CastKind CK = DestType->isPointerType() ? CK_NullToPointer : CK_BitCast; + SrcExpr = ImplicitCastExpr::Create(Self.Context, DestType, CK, + SrcExpr.get(), nullptr, VK_PRValue, + Self.CurFPFeatureOverrides()); + } + } + if (DestType->isNullPtrType() && !SrcType->isNullPtrType()) { + Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_nullptr_cast) + << /*type to nullptr*/ 1 << SrcType; + SrcExpr = ExprError(); + return; + } + if (DestType->isExtVectorType()) { SrcExpr = Self.CheckExtVectorCast(OpRange, DestType, SrcExpr.get(), Kind); return; diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -2645,6 +2645,13 @@ Results.AddResult(Result(Builder.TakeString())); } + if (SemaRef.getLangOpts().C2x) { + // nullptr + Builder.AddResultTypeChunk("nullptr_t"); + Builder.AddTypedTextChunk("nullptr"); + Results.AddResult(Result(Builder.TakeString())); + } + // sizeof expression Builder.AddResultTypeChunk("size_t"); Builder.AddTypedTextChunk("sizeof"); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16780,11 +16780,10 @@ AllowFoldKind FoldKind = NoFold; if (!getLangOpts().CPlusPlus) { - // In C mode only allow folding and strip the implicit conversion - // to the type of the first _Static_assert argument that would - // otherwise suppress diagnostics for arguments that convert to int. + // In C mode, allow folding as an extension for better compatibility with + // C++ in terms of expressions like static_assert("test") or + // static_assert(nullptr). FoldKind = AllowFold; - BaseExpr = BaseExpr->IgnoreImpCasts(); } if (!Failed && VerifyIntegerConstantExpression( diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8724,6 +8724,12 @@ return ResTy; } + // C2x 6.5.15p7: + // ... if both the second and third operands have nullptr_t type, the + // result also has that type. + if (LHSTy->isNullPtrType() && Context.hasSameType(LHSTy, RHSTy)) + return ResTy; + // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has // the type of the other operand." if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; @@ -9981,6 +9987,24 @@ return Incompatible; } + // This check seems unnatural, however it is necessary to ensure the proper + // conversion of functions/arrays. If the conversion were done for all + // DeclExpr's (created by ActOnIdExpression), it would mess up the unary + // expressions that suppress this implicit conversion (&, sizeof). This needs + // to happen before we check for null pointer conversions because C does not + // undergo the same implicit conversions as C++ does above (by the calls to + // TryImplicitConversion() and PerformImplicitConversion()) which insert the + // lvalue to rvalue cast before checking for null pointer constraints. This + // addresses code like: nullptr_t val; int *ptr; ptr = val; + // + // Suppress this for references: C++ 8.5.3p5. + if (!LHSType->isReferenceType()) { + // FIXME: We potentially allocate here even if ConvertRHS is false. + RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); + if (RHS.isInvalid()) + return Incompatible; + } + // C99 6.5.16.1p1: the left operand is a pointer and the right is // a null pointer constant. if ((LHSType->isPointerType() || LHSType->isObjCObjectPointerType() || @@ -10005,18 +10029,6 @@ return Compatible; } - // This check seems unnatural, however it is necessary to ensure the proper - // conversion of functions/arrays. If the conversion were done for all - // DeclExpr's (created by ActOnIdExpression), it would mess up the unary - // expressions that suppress this implicit conversion (&, sizeof). - // - // Suppress this for references: C++ 8.5.3p5. - if (!LHSType->isReferenceType()) { - // FIXME: We potentially allocate here even if ConvertRHS is false. - RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); - if (RHS.isInvalid()) - return Incompatible; - } CastKind Kind; Sema::AssignConvertType result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); @@ -12571,34 +12583,54 @@ return computeResultTy(); } - if (getLangOpts().CPlusPlus) { - // C++ [expr.eq]p4: - // Two operands of type std::nullptr_t or one operand of type - // std::nullptr_t and the other a null pointer constant compare equal. - if (!IsOrdered && LHSIsNull && RHSIsNull) { - if (LHSType->isNullPtrType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (RHSType->isNullPtrType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } - } - // Comparison of Objective-C pointers and block pointers against nullptr_t. - // These aren't covered by the composite pointer type rules. - if (!IsOrdered && RHSType->isNullPtrType() && - (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { + // C++ [expr.eq]p4: + // Two operands of type std::nullptr_t or one operand of type + // std::nullptr_t and the other a null pointer constant compare + // equal. + // C2x 6.5.9p5: + // If both operands have type nullptr_t or one operand has type nullptr_t + // and the other is a null pointer constant, they compare equal. + if (!IsOrdered && LHSIsNull && RHSIsNull) { + if (LHSType->isNullPtrType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); return computeResultTy(); } - if (!IsOrdered && LHSType->isNullPtrType() && - (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { + if (RHSType->isNullPtrType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + } + + if (!getLangOpts().CPlusPlus && !IsOrdered && (LHSIsNull || RHSIsNull)) { + // C2x 6.5.9p6: + // Otherwise, at least one operand is a pointer. If one is a pointer and + // the other is a null pointer constant, the null pointer constant is + // converted to the type of the pointer. + if (LHSIsNull && RHSType->isPointerType()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); return computeResultTy(); } + if (RHSIsNull && LHSType->isPointerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + } + + // Comparison of Objective-C pointers and block pointers against nullptr_t. + // These aren't covered by the composite pointer type rules. + if (!IsOrdered && RHSType->isNullPtrType() && + (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (!IsOrdered && LHSType->isNullPtrType() && + (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + if (getLangOpts().CPlusPlus) { if (IsRelational && ((LHSType->isNullPtrType() && RHSType->isPointerType()) || (RHSType->isNullPtrType() && LHSType->isPointerType()))) { diff --git a/clang/test/C/C11/n1330.c b/clang/test/C/C11/n1330.c --- a/clang/test/C/C11/n1330.c +++ b/clang/test/C/C11/n1330.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify %s +// RUN: %clang_cc1 -verify -Wgnu-folding-constant %s /* WG14 N1330: Yes * Static assertions @@ -55,7 +55,7 @@ // Ensure that only an integer constant expression can be used as the // controlling expression. - _Static_assert(1.0f, "this should not compile"); // expected-error {{static assertion expression is not an integral constant expression}} + _Static_assert(1.0f, "this should not compile"); // expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} } // FIXME: This is using the placeholder date Clang produces for the macro in diff --git a/clang/test/C/C2x/n3042.c b/clang/test/C/C2x/n3042.c new file mode 100644 --- /dev/null +++ b/clang/test/C/C2x/n3042.c @@ -0,0 +1,184 @@ +// RUN: %clang_cc1 -verify -ffreestanding -Wno-unused -std=c2x %s + +/* WG14 N3042: partial + * Introduce the nullptr constant + * + * Claiming partial support for this feature until the WG14 NB comments can be + * resolved to know what the correct behavior really should be. + */ + +#include + +// FIXME: The paper calls for a feature testing macro to be added to stddef.h +// which we do not implement. This should be addressed after WG14 has processed +// national body comments for C2x as we've asked for the feature test macros to +// be removed. +#ifndef __STDC_VERSION_STDDEF_H__ +#error "no version macro for stddef.h" +#endif +// expected-error@-2 {{"no version macro for stddef.h"}} + +void questionable_behaviors() { + nullptr_t val; + + // FIXME: This code is intended to be rejected by C and is accepted by C++. + // We've filed an NB comment with WG14 about the incompatibility. + (void)(1 ? val : 0); // expected-error {{non-pointer operand type 'int' incompatible with nullptr}} + (void)(1 ? nullptr : 0); // expected-error {{non-pointer operand type 'int' incompatible with nullptr}} + + // FIXME: This code is intended to be accepted by C and is rejected by C++. + // We're following the C++ semantics until WG14 has resolved the NB comments + // we've filed about the incompatibility. + _Bool another = val; // expected-error {{initializing 'bool' with an expression of incompatible type 'nullptr_t'}} + another = val; // expected-error {{assigning to 'bool' from incompatible type 'nullptr_t'}} + _Bool again = nullptr; // expected-error {{initializing 'bool' with an expression of incompatible type 'nullptr_t'}} + again = nullptr; // expected-error {{assigning to 'bool' from incompatible type 'nullptr_t'}} + + // FIXME: This code is intended to be rejected by C and is accepted by C++. + // We've filed an NB comment with WG14 about the incompatibility. + val = 0; // expected-error {{assigning to 'nullptr_t' from incompatible type 'int'}} + + // Not accepted in C++ but might want to accept in C as a null pointer constant? + val = (void *)0; // expected-error {{assigning to 'nullptr_t' from incompatible type 'void *'}} +} + +void test() { + // Can we declare the type? + nullptr_t null_val; + + // Can we use the keyword? + int *typed_ptr = nullptr; + typed_ptr = nullptr; + + // Can we use the keyword with the type? + null_val = nullptr; + // Even initialize with it? + nullptr_t ignore = nullptr; + + // Can we assign an object of the type to another object of the same type? + null_val = null_val; + + // Can we assign nullptr_t objects to pointer objects? + typed_ptr = null_val; + + // Can we take the address of an object of type nullptr_t? + &null_val; + + // How about the null pointer named constant? + &nullptr; // expected-error {{cannot take the address of an rvalue of type 'nullptr_t'}} + + // Can it be used in all the places a scalar can be used? + if (null_val) {} + if (!null_val) {} + for (;null_val;) {} + while (nullptr) {} + null_val && nullptr; + nullptr || null_val; + null_val ? 0 : 1; + sizeof(null_val); + alignas(nullptr_t) int aligned; + + // Cast expressions have special handling for nullptr_t despite allowing + // casts of scalar types. + (nullptr_t)12; // expected-error {{cannot cast an object of type 'int' to 'nullptr_t'}} + (float)null_val; // expected-error {{cannot cast an object of type 'nullptr_t' to 'float'}} + (float)nullptr; // expected-error {{cannot cast an object of type 'nullptr_t' to 'float'}} + (nullptr_t)0; // expected-error {{cannot cast an object of type 'int' to 'nullptr_t'}} + (nullptr_t)(void *)0; // expected-error {{cannot cast an object of type 'void *' to 'nullptr_t'}} + (nullptr_t)(int *)12; // expected-error {{cannot cast an object of type 'int *' to 'nullptr_t'}} + + (void)null_val; // ok + (void)nullptr; // ok + (bool)null_val; // ok + (bool)nullptr; // ok + (int *)null_val; // ok + (int *)nullptr; // ok + (nullptr_t)nullptr; // ok + + // Can it be converted to bool with the result false (this relies on Clang + // accepting additional kinds of constant expressions where an ICE is + // required)? + static_assert(!nullptr); + static_assert(!null_val); + static_assert(nullptr); // expected-error {{static assertion failed due to requirement 'nullptr'}} \ + expected-warning {{implicit conversion of nullptr constant to 'bool'}} + static_assert(null_val); // expected-error {{static assertion failed due to requirement 'null_val'}} \ + expected-warning {{implicit conversion of nullptr constant to 'bool'}} + + // Do equality operators work as expected with it? + static_assert(nullptr == nullptr); + static_assert(null_val == null_val); + static_assert(nullptr != (int*)1); + static_assert(null_val != (int*)1); + static_assert(nullptr == null_val); + static_assert(nullptr == 0); + static_assert(null_val == (void *)0); + + // None of the relational operators should succeed. + (void)(null_val <= 0); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'int')}} + (void)(null_val >= (void *)0); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'void *')}} + (void)(!(null_val < (void *)0)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'void *')}} + (void)(!(null_val > 0)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'int')}} + (void)(nullptr <= 0); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'int')}} + (void)(nullptr >= (void *)0); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'void *')}} + (void)(!(nullptr < (void *)0)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'void *')}} + (void)(!(nullptr > 0)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'int')}} + (void)(null_val <= null_val); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(null_val >= null_val); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(null_val < null_val)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(null_val > null_val)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(null_val <= nullptr); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(null_val >= nullptr); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(null_val < nullptr)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(null_val > nullptr)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(nullptr <= nullptr); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(nullptr >= nullptr); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(nullptr < nullptr)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + (void)(!(nullptr > nullptr)); // expected-error {{invalid operands to binary expression ('nullptr_t' and 'nullptr_t')}} + + // Do we pick the correct common type for conditional operators? + _Generic(1 ? nullptr : nullptr, nullptr_t : 0); + _Generic(1 ? null_val : null_val, nullptr_t : 0); + _Generic(1 ? typed_ptr : null_val, typeof(typed_ptr) : 0); + _Generic(1 ? null_val : typed_ptr, typeof(typed_ptr) : 0); + _Generic(1 ? nullptr : typed_ptr, typeof(typed_ptr) : 0); + _Generic(1 ? typed_ptr : nullptr, typeof(typed_ptr) : 0); + + // Same for GNU conditional operators? + _Generic(nullptr ?: nullptr, nullptr_t : 0); + _Generic(null_val ?: null_val, nullptr_t : 0); + _Generic(typed_ptr ?: null_val, typeof(typed_ptr) : 0); + _Generic(null_val ?: typed_ptr, typeof(typed_ptr) : 0); + _Generic(nullptr ?: typed_ptr, typeof(typed_ptr) : 0); + _Generic(typed_ptr ?: nullptr, typeof(typed_ptr) : 0); + + // Do we correctly issue type incompatibility diagnostics? + int i = nullptr; // expected-error {{initializing 'int' with an expression of incompatible type 'nullptr_t'}} + float f = nullptr; // expected-error {{initializing 'float' with an expression of incompatible type 'nullptr_t'}} + i = null_val; // expected-error {{assigning to 'int' from incompatible type 'nullptr_t'}} + f = null_val; // expected-error {{assigning to 'float' from incompatible type 'nullptr_t'}} + null_val = i; // expected-error {{assigning to 'nullptr_t' from incompatible type 'int'}} + null_val = f; // expected-error {{assigning to 'nullptr_t' from incompatible type 'float'}} +} + +// Can we use it as a function parameter? +void null_param(nullptr_t); // expected-note 2 {{passing argument to parameter here}} + +void other_test() { + // Can we call the function properly? + null_param(nullptr); + + // Do we get reasonable diagnostics when we can't call the function? + null_param((void *)0); // expected-error {{passing 'void *' to parameter of incompatible type 'nullptr_t'}} + + // FIXME: The paper requires this to be rejected, but it is accepted in C++. + // This should be addressed after WG14 has processed national body comments. + null_param(0); // expected-error {{passing 'int' to parameter of incompatible type 'nullptr_t'}} +} + + +void printf(const char*, ...) __attribute__((format(printf, 1, 2))); +void format_specifiers() { + // Don't warn when using nullptr with %p. + printf("%p", nullptr); +} diff --git a/clang/test/Sema/nullptr-prec2x.c b/clang/test/Sema/nullptr-prec2x.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/nullptr-prec2x.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c17 -ffreestanding -Wc2x-compat %s + +#include + +int nullptr; // expected-warning {{'nullptr' is a keyword in C2x}} + +nullptr_t val; // expected-error {{unknown type name 'nullptr_t'}} + diff --git a/clang/test/Sema/nullptr.c b/clang/test/Sema/nullptr.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/nullptr.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c2x -ffreestanding -Wno-null-conversion -Wno-tautological-compare %s +#include + +typedef typeof(nullptr) nullptr_t; + +struct A {}; + +__attribute__((overloadable)) int o1(char*); +__attribute__((overloadable)) void o1(uintptr_t); + +nullptr_t f(nullptr_t null) +{ + // Implicit conversions. + null = nullptr; + void *p = nullptr; + p = null; + int *pi = nullptr; + pi = null; + null = 0; // expected-error {{assigning to 'nullptr_t' from incompatible type 'int'}} + bool b = nullptr; // expected-error {{initializing 'bool' with an expression of incompatible type 'nullptr_t'}} + + // Can't convert nullptr to integral implicitly. + uintptr_t i = nullptr; // expected-error-re {{initializing 'uintptr_t' (aka '{{.*}}') with an expression of incompatible type 'nullptr_t'}} + + // Operators + (void)(null == nullptr); + (void)(null <= nullptr); // expected-error {{invalid operands to binary expression}} + (void)(null == 0); + (void)(null == (void*)0); + (void)((void*)0 == nullptr); + (void)(null <= 0); // expected-error {{invalid operands to binary expression}} + (void)(null <= (void*)0); // expected-error {{invalid operands to binary expression}} + (void)((void*)0 <= nullptr); // expected-error {{invalid operands to binary expression}} + (void)(0 == nullptr); + (void)(nullptr == 0); + (void)(nullptr <= 0); // expected-error {{invalid operands to binary expression}} + (void)(0 <= nullptr); // expected-error {{invalid operands to binary expression}} + (void)(1 > nullptr); // expected-error {{invalid operands to binary expression}} + (void)(1 != nullptr); // expected-error {{invalid operands to binary expression}} + (void)(1 + nullptr); // expected-error {{invalid operands to binary expression}} + (void)(0 ? nullptr : 0); // expected-error {{non-pointer operand type 'int' incompatible with nullptr}} + (void)(0 ? nullptr : (void*)0); + (void)(0 ? nullptr : (struct A){}); // expected-error {{non-pointer operand type 'struct A' incompatible with nullptr}} + (void)(0 ? (struct A){} : nullptr); // expected-error {{non-pointer operand type 'struct A' incompatible with nullptr}} + + // Overloading + int t = o1(nullptr); + t = o1(null); + + // nullptr is an rvalue, null is an lvalue + (void)&nullptr; // expected-error {{cannot take the address of an rvalue of type 'nullptr_t'}} + nullptr_t *pn = &null; + + int *ip = *pn; + if (*pn) { } +} + +__attribute__((overloadable)) void *g(void*); +__attribute__((overloadable)) bool g(bool); + +// Test that we prefer g(void*) over g(bool). +static_assert(__builtin_types_compatible_p(typeof(g(nullptr)), void *), ""); + +void sent(int, ...) __attribute__((sentinel)); + +void g() { + // nullptr can be used as the sentinel value. + sent(10, nullptr); +} + +void printf(const char*, ...) __attribute__((format(printf, 1, 2))); + +void h() { + // Don't warn when using nullptr with %p. + printf("%p", nullptr); +} + +static_assert(sizeof(nullptr_t) == sizeof(void*), ""); + +static_assert(!(nullptr < nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(nullptr > nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr <= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr >= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr == nullptr, ""); +static_assert(!(nullptr != nullptr), ""); + +static_assert(!(0 < nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(0 > nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert( 0 <= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( 0 >= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( 0 == nullptr, ""); +static_assert(!(0 != nullptr), ""); + +static_assert(!(nullptr < 0), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(nullptr > 0), ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr <= 0, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr >= 0, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr == 0, ""); +static_assert(!(nullptr != 0), ""); + +__attribute__((overloadable)) int f1(int*); +__attribute__((overloadable)) float f1(bool); + +void test_f1() { + int ir = (f1)(nullptr); +} + diff --git a/clang/test/Sema/static-assert.c b/clang/test/Sema/static-assert.c --- a/clang/test/Sema/static-assert.c +++ b/clang/test/Sema/static-assert.c @@ -1,11 +1,11 @@ -// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify %s -// RUN: %clang_cc1 -fms-compatibility -DMS -fsyntax-only -verify=expected,ms %s -// RUN: %clang_cc1 -std=c99 -pedantic -fsyntax-only -verify=expected,ext %s +// RUN: %clang_cc1 -std=c11 -Wgnu-folding-constant -fsyntax-only -verify %s +// RUN: %clang_cc1 -fms-compatibility -Wgnu-folding-constant -DMS -fsyntax-only -verify=expected,ms %s +// RUN: %clang_cc1 -std=c99 -pedantic -Wgnu-folding-constant -fsyntax-only -verify=expected,ext %s // RUN: %clang_cc1 -xc++ -std=c++11 -pedantic -fsyntax-only -verify=expected,ext,cxx %s _Static_assert("foo", "string is nonzero"); // ext-warning {{'_Static_assert' is a C11 extension}} #ifndef __cplusplus -// expected-error@-2 {{static assertion expression is not an integral constant expression}} +// expected-warning@-2 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} #endif _Static_assert(1, "1 is nonzero"); // ext-warning {{'_Static_assert' is a C11 extension}} @@ -85,12 +85,12 @@ _Static_assert(1.0 != 0, ""); // ext-warning {{'_Static_assert' is a C11 extension}} _Static_assert(__builtin_strlen("1"), ""); // ext-warning {{'_Static_assert' is a C11 extension}} #ifndef __cplusplus -// ext-warning@-9 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} -// ext-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} -// ext-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} -// ext-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} -// ext-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} -// ext-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-9 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} +// expected-warning@-8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} // __builtin_strlen(literal) is considered an integer constant expression // and doesn't cause a pedantic warning #endif diff --git a/clang/www/c_status.html b/clang/www/c_status.html --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -1204,7 +1204,13 @@ Introduce the nullptr constant N3042 - No + +
Partial + Parts of the implementation may be incorrect until WG14 has completed NB comment + resolution for incompatibilities with C++ that were discovered. The major use cases + and usage patterns should work well, though. +
+ Memory layout of unions