Skip to content

Conversation

@AaronBallman
Copy link
Collaborator

C23 allows a cast of a null pointer constant to nullptr_t. e.g., (nullptr_t)0 or (nullptr_t)(void *)0.

Fixes #133644

C23 allows a cast of a null pointer constant to nullptr_t. e.g.,
(nullptr_t)0 or (nullptr_t)(void *)0.

Fixes llvm#133644
@AaronBallman AaronBallman added clang Clang issues not falling into any other category c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" rejects-valid labels Mar 31, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 31, 2025

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

C23 allows a cast of a null pointer constant to nullptr_t. e.g., (nullptr_t)0 or (nullptr_t)(void *)0.

Fixes #133644


Full diff: https://github.com/llvm/llvm-project/pull/133742.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/lib/Sema/SemaCast.cpp (+18-5)
  • (modified) clang/test/C/C23/n3042.c (+3-2)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4b8e09d051616..5fdbec3eb4443 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -159,6 +159,8 @@ C23 Feature Support
   which clarified that a compound literal used within a function prototype is
   treated as if the compound literal were within the body rather than at file
   scope.
+- Fixed a bug where you could not cast a null pointer constant to type
+  ``nullptr_t``. Fixes #GH133644.
 
 Non-comprehensive list of changes in this release
 -------------------------------------------------
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index a5dbc16eaea0b..2087815abe9e4 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3115,11 +3115,24 @@ void CastOperation::CheckCStyleCast() {
                                          Self.CurFPFeatureOverrides());
     }
   }
-  if (DestType->isNullPtrType() && !SrcType->isNullPtrType()) {
-    Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_nullptr_cast)
-        << /*type to nullptr*/ 1 << SrcType;
-    SrcExpr = ExprError();
-    return;
+  // C23 6.3.2.4p2: a null pointer constant or value of type nullptr_t may be
+  // converted to nullptr_t.
+  if (DestType->isNullPtrType()) {
+    if (!SrcType->isNullPtrType() &&
+        !SrcExpr.get()->isNullPointerConstant(Self.Context,
+                                              Expr::NPC_NeverValueDependent)) {
+      Self.Diag(SrcExpr.get()->getExprLoc(), diag::err_nullptr_cast)
+          << /*type to nullptr*/ 1 << SrcType;
+      SrcExpr = ExprError();
+      return;
+    }
+    if (!SrcType->isNullPtrType()) {
+      // Need to convert the source from whatever its type is to a null pointer
+      // type first.
+      SrcExpr = ImplicitCastExpr::Create(
+          Self.Context, DestType, CK_NullToPointer, SrcExpr.get(), nullptr,
+          VK_PRValue, Self.CurFPFeatureOverrides());
+    }
   }
 
   if (DestType->isExtVectorType()) {
diff --git a/clang/test/C/C23/n3042.c b/clang/test/C/C23/n3042.c
index 99661b1fb39eb..fdcb48eb1322a 100644
--- a/clang/test/C/C23/n3042.c
+++ b/clang/test/C/C23/n3042.c
@@ -82,8 +82,6 @@ void test() {
   (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
@@ -93,6 +91,9 @@ void test() {
   (int *)null_val;    // ok
   (int *)nullptr;     // ok
   (nullptr_t)nullptr; // ok
+  (nullptr_t)0;       // ok
+  (nullptr_t)(void *)0; // ok
+  (nullptr_t)null_val;  // 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

@AaronBallman AaronBallman requested review from Sirraide and jyknight and removed request for jyknight April 1, 2025 19:47
@AaronBallman AaronBallman merged commit 574e43d into llvm:main Apr 2, 2025
12 checks passed
@AaronBallman AaronBallman deleted the aballman-fix-c23-nullptr branch April 2, 2025 11:28
(int *)null_val; // ok
(int *)nullptr; // ok
(nullptr_t)nullptr; // ok
(nullptr_t)0; // ok
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth checking other literals as well, like floating point, char and string literals?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added that test coverage in fb9deab, thanks for the suggestion!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c23 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category rejects-valid

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cast from null pointer constant to nullptr is failing.

5 participants