[clang] 3a31970 - [C2x] Implement support for nullptr and nullptr_t

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 14 07:06:14 PDT 2022


Author: Aaron Ballman
Date: 2022-10-14T10:06:02-04:00
New Revision: 3a31970ee2af24ffcfd4b6fc11ac9bdf4d4ef2e8

URL: https://github.com/llvm/llvm-project/commit/3a31970ee2af24ffcfd4b6fc11ac9bdf4d4ef2e8
DIFF: https://github.com/llvm/llvm-project/commit/3a31970ee2af24ffcfd4b6fc11ac9bdf4d4ef2e8.diff

LOG: [C2x] Implement support for nullptr and nullptr_t

This introduces support for nullptr and nullptr_t in C2x mode. The
proposal accepted by WG14 is:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3042.htm

Note, there are quite a few incompatibilities with the C++ feature in
some of the edge cases of this feature. Therefore, there are some FIXME
comments in tests for testing behavior that might change after WG14 has
resolved national body comments (a process we've not yet started). So
this implementation might change slightly depending on the resolution
of comments. This is called out explicitly in the release notes as
well.

Differential Revision: https://reviews.llvm.org/D135099

Added: 
    clang/test/C/C2x/n3042.c
    clang/test/Sema/nullptr-prec2x.c
    clang/test/Sema/nullptr.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/PrettyPrinter.h
    clang/include/clang/AST/Type.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/TokenKinds.def
    clang/lib/AST/Expr.cpp
    clang/lib/AST/Type.cpp
    clang/lib/Headers/stddef.h
    clang/lib/Parse/ParseExpr.cpp
    clang/lib/Sema/SemaCast.cpp
    clang/lib/Sema/SemaCodeComplete.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/test/C/C11/n1330.c
    clang/test/Sema/static-assert.c
    clang/www/c_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a81877f27b71..246025ee0a282 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -452,6 +452,32 @@ C2x Feature Support
     typeof(Val) OtherVal; // type is '__attribute__((address_space(1))) const _Atomic int'
     typeof_unqual(Val) OtherValUnqual; // type is 'int'
 
+- Implemented `WG14 N3042 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3042.htm>`_,
+  Introduce the nullptr constant. This introduces a new type ``nullptr_t``,
+  declared in ``<stddef.h>`` 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
index 104fd92784e74..12065828df102 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -761,6 +761,8 @@ class CXXBoolLiteralExpr : public Expr {
 /// 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
index cb25b2750dd43..5aeaca7beda2f 100644
--- a/clang/include/clang/AST/PrettyPrinter.h
+++ b/clang/include/clang/AST/PrettyPrinter.h
@@ -65,7 +65,8 @@ struct PrintingPolicy {
         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 @@ struct PrintingPolicy {
   /// 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
index fbf65e588fb51..f95329e41fa53 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2223,7 +2223,8 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
   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
index d3605d9bcba7d..28da4ff72bc45 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -702,6 +702,11 @@ def warn_cxx98_compat_noexcept_expr : Warning<
   InGroup<CXX98Compat>, DefaultIgnore;
 def warn_cxx98_compat_nullptr : Warning<
   "'nullptr' is incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore;
+def ext_c_nullptr : Extension<
+  "'nullptr' is a C2x extension">, InGroup<C2x>;
+def warn_c17_compat_nullptr : Warning<
+  "'nullptr' is incompatible with C standards before C2x">,
+  InGroup<CPre2xCompat>, 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
index 3e9fe53eea8ea..2bae254a097d9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8694,6 +8694,9 @@ def warn_cast_function_type : Warning<
   InGroup<CastFunctionType>, 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
index 1127dc88c3122..dbff0412a045f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -390,7 +390,7 @@ CXX11_KEYWORD(char32_t              , KEYNOMS18)
 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
index 08b5e8d45c95a..3fb780adc8bc7 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3930,7 +3930,7 @@ Expr::isNullPointerConstant(ASTContext &Ctx,
   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
index bd5fe3dda304d..a170542349ab2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3084,7 +3084,7 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
   case Char32:
     return "char32_t";
   case NullPtr:
-    return "std::nullptr_t";
+    return Policy.NullptrTypeInNamespace ? "std::nullptr_t" : "nullptr_t";
   case Overload:
     return "<overloaded function type>";
   case BoundMember:

diff  --git a/clang/lib/Headers/stddef.h b/clang/lib/Headers/stddef.h
index 8e86a21e8dbed..42815176dcd0f 100644
--- a/clang/lib/Headers/stddef.h
+++ b/clang/lib/Headers/stddef.h
@@ -97,6 +97,12 @@ using ::std::nullptr_t;
 #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
index b8f351dbbf1e1..3ad1a17a66818 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1003,7 +1003,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
     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
index 7b5bc7ca80b17..d21c5b7d10270 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -2985,6 +2985,37 @@ void CastOperation::CheckCStyleCast() {
     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
index c8eac7b06b652..efa78ace8d376 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -2645,6 +2645,13 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S,
       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
index 27a61c27e6e4b..280af69da6a2b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16780,11 +16780,10 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
     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
index 474f86cffd169..7faae7e7ee430 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8724,6 +8724,12 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
     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 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
       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 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
     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 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     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
index 4208464610b64..b8f31a4dbc6ff 100644
--- 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 @@ void test(void) {
 
   // 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
index 0000000000000..e949890bf82f1
--- /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 <stddef.h>
+
+// 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 at -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
index 0000000000000..387746f20a3ea
--- /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 <stddef.h>
+
+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
index 0000000000000..46a9111ee278e
--- /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 <stdint.h>
+
+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
index a669120cb8379..6ede89dc00690 100644
--- 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 at -2 {{static assertion expression is not an integral constant expression}}
+// expected-warning at -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], "");            // ext-warning {{'_Static_assert' is a C1
 _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 at -9 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-// ext-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-// ext-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-// ext-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-// ext-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-// ext-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -9 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -8 {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+// expected-warning at -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
index db65a40d6858a..d2cefc87dbd35 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1204,7 +1204,13 @@ <h2 id="c2x">C2x implementation status</h2>
     <tr>
       <td>Introduce the nullptr constant</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3042.htm">N3042</a></td>
-      <td class="none" align="center">No</td>
+      <td class="partial" align="center">
+        <details><summary>Partial</summary>
+          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.
+        </details>
+      </td>
     </tr>
     <tr>
       <td>Memory layout of unions</td>


        


More information about the cfe-commits mailing list