Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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()) {
Expand Down
6 changes: 2 additions & 4 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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 {
Expand Down
18 changes: 17 additions & 1 deletion clang/test/AST/ByteCode/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading