[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