[clang] [clang][bytecode] Allow up/down casts of nullptr (PR #127615)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 18 02:51:40 PST 2025


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/127615

If the target type is a pointer type.

>From c8593a44e4589cc5267070fbb7d9b758390ef792 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 18 Feb 2025 11:49:02 +0100
Subject: [PATCH] [clang][bytecode] Allow up/down casts of nullptr

If the target type is a pointer type.
---
 clang/lib/AST/ByteCode/Compiler.cpp |  6 ++++--
 clang/lib/AST/ByteCode/Interp.cpp   |  2 +-
 clang/lib/AST/ByteCode/Interp.h     | 19 +++++++++++++++----
 clang/lib/AST/ByteCode/Opcodes.td   |  6 ++----
 clang/test/AST/ByteCode/records.cpp | 18 +++++++++++++++++-
 5 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 59c236c9da8c8..c58e884c77d6b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -272,7 +272,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
         CurType = B->getType();
       } else {
         unsigned DerivedOffset = collectBaseOffset(B->getType(), CurType);
-        if (!this->emitGetPtrBasePop(DerivedOffset, CE))
+        if (!this->emitGetPtrBasePop(
+                DerivedOffset, /*NullOK=*/CE->getType()->isPointerType(), CE))
           return false;
         CurType = B->getType();
       }
@@ -288,7 +289,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
     unsigned DerivedOffset =
         collectBaseOffset(SubExpr->getType(), CE->getType());
 
-    return this->emitGetPtrDerivedPop(DerivedOffset, CE);
+    return this->emitGetPtrDerivedPop(
+        DerivedOffset, /*NullOK=*/CE->getType()->isPointerType(), CE);
   }
 
   case CK_FloatingCast: {
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index c80be094856b0..f09081101f086 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1432,7 +1432,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
     unsigned Offset = S.getContext().collectBaseOffset(
         InitialPointeeType->getAsRecordDecl(),
         OverriderPointeeType->getAsRecordDecl());
-    return GetPtrBasePop(S, OpPC, Offset);
+    return GetPtrBasePop(S, OpPC, Offset, /*IsNullOK=*/true);
   }
 
   return true;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 73cc107b7dbff..0f24c83d03029 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1568,10 +1568,20 @@ inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
   return true;
 }
 
-inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
+inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
+                             bool NullOK) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
-  if (!CheckNull(S, OpPC, Ptr, CSK_Derived))
+  if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
     return false;
+
+  if (!Ptr.isBlockPointer()) {
+    // FIXME: We don't have the necessary information in integral pointers.
+    // The Descriptor only has a record, but that does of course not include
+    // the potential derived classes of said record.
+    S.Stk.push<Pointer>(Ptr);
+    return true;
+  }
+
   if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
     return false;
   if (!CheckDowncast(S, OpPC, Ptr, Off))
@@ -1600,10 +1610,11 @@ inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
   return true;
 }
 
-inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
+inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off,
+                          bool NullOK) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
 
-  if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+  if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base))
     return false;
 
   if (!Ptr.isBlockPointer()) {
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 088a3e40fe2a7..41e4bae65c195 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -312,7 +312,7 @@ def GetPtrThisField : OffsetOpcode;
 // [Pointer] -> [Pointer]
 def GetPtrBase : OffsetOpcode;
 // [Pointer] -> [Pointer]
-def GetPtrBasePop : OffsetOpcode;
+def GetPtrBasePop : OffsetOpcode { let Args = [ArgUint32, ArgBool]; }
 def GetMemberPtrBasePop : Opcode {
   // Offset of field, which is a base.
   let Args = [ArgSint32];
@@ -322,9 +322,7 @@ def GetMemberPtrBasePop : Opcode {
 def FinishInitPop : Opcode;
 def FinishInit    : Opcode;
 
-def GetPtrDerivedPop : Opcode {
-  let Args = [ArgUint32];
-}
+def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, ArgBool]; }
 
 // [Pointer] -> [Pointer]
 def GetPtrVirtBasePop : Opcode {
diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp
index 9470e7d8e3dcb..3cc3210841e0f 100644
--- a/clang/test/AST/ByteCode/records.cpp
+++ b/clang/test/AST/ByteCode/records.cpp
@@ -1656,12 +1656,28 @@ namespace ExprWithCleanups {
   static_assert(F == 1i, "");
 }
 
-namespace NullptrUpcast {
+namespace NullptrCast {
   struct A {};
   struct B : A { int n; };
+  constexpr A *na = nullptr;
   constexpr B *nb = nullptr;
   constexpr A &ra = *nb; // both-error {{constant expression}} \
                          // both-note {{cannot access base class of null pointer}}
+  constexpr B &rb = (B&)*na; // both-error {{constant expression}} \
+                             // both-note {{cannot access derived class of null pointer}}
+  constexpr bool test() {
+    auto a = (A*)(B*)nullptr;
+
+    return a == nullptr;
+  }
+  static_assert(test(), "");
+
+  constexpr bool test2() {
+    auto a = (B*)(A*)nullptr;
+
+    return a == nullptr;
+  }
+  static_assert(test2(), "");
 }
 
 namespace NonConst {



More information about the cfe-commits mailing list