Skip to content

Commit 5fbb6d9

Browse files
authored
[clang][bytecode] Allow up/down casts of nullptr (#127615)
If the target type is a pointer type.
1 parent 09c2441 commit 5fbb6d9

File tree

5 files changed

+39
-12
lines changed

5 files changed

+39
-12
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
272272
CurType = B->getType();
273273
} else {
274274
unsigned DerivedOffset = collectBaseOffset(B->getType(), CurType);
275-
if (!this->emitGetPtrBasePop(DerivedOffset, CE))
275+
if (!this->emitGetPtrBasePop(
276+
DerivedOffset, /*NullOK=*/CE->getType()->isPointerType(), CE))
276277
return false;
277278
CurType = B->getType();
278279
}
@@ -288,7 +289,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
288289
unsigned DerivedOffset =
289290
collectBaseOffset(SubExpr->getType(), CE->getType());
290291

291-
return this->emitGetPtrDerivedPop(DerivedOffset, CE);
292+
return this->emitGetPtrDerivedPop(
293+
DerivedOffset, /*NullOK=*/CE->getType()->isPointerType(), CE);
292294
}
293295

294296
case CK_FloatingCast: {

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
14331433
unsigned Offset = S.getContext().collectBaseOffset(
14341434
InitialPointeeType->getAsRecordDecl(),
14351435
OverriderPointeeType->getAsRecordDecl());
1436-
return GetPtrBasePop(S, OpPC, Offset);
1436+
return GetPtrBasePop(S, OpPC, Offset, /*IsNullOK=*/true);
14371437
}
14381438

14391439
return true;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,10 +1568,20 @@ inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
15681568
return true;
15691569
}
15701570

1571-
inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
1571+
inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
1572+
bool NullOK) {
15721573
const Pointer &Ptr = S.Stk.pop<Pointer>();
1573-
if (!CheckNull(S, OpPC, Ptr, CSK_Derived))
1574+
if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
15741575
return false;
1576+
1577+
if (!Ptr.isBlockPointer()) {
1578+
// FIXME: We don't have the necessary information in integral pointers.
1579+
// The Descriptor only has a record, but that does of course not include
1580+
// the potential derived classes of said record.
1581+
S.Stk.push<Pointer>(Ptr);
1582+
return true;
1583+
}
1584+
15751585
if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
15761586
return false;
15771587
if (!CheckDowncast(S, OpPC, Ptr, Off))
@@ -1600,10 +1610,11 @@ inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
16001610
return true;
16011611
}
16021612

1603-
inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
1613+
inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off,
1614+
bool NullOK) {
16041615
const Pointer &Ptr = S.Stk.pop<Pointer>();
16051616

1606-
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
1617+
if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base))
16071618
return false;
16081619

16091620
if (!Ptr.isBlockPointer()) {

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def GetPtrThisField : OffsetOpcode;
312312
// [Pointer] -> [Pointer]
313313
def GetPtrBase : OffsetOpcode;
314314
// [Pointer] -> [Pointer]
315-
def GetPtrBasePop : OffsetOpcode;
315+
def GetPtrBasePop : OffsetOpcode { let Args = [ArgUint32, ArgBool]; }
316316
def GetMemberPtrBasePop : Opcode {
317317
// Offset of field, which is a base.
318318
let Args = [ArgSint32];
@@ -322,9 +322,7 @@ def GetMemberPtrBasePop : Opcode {
322322
def FinishInitPop : Opcode;
323323
def FinishInit : Opcode;
324324

325-
def GetPtrDerivedPop : Opcode {
326-
let Args = [ArgUint32];
327-
}
325+
def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, ArgBool]; }
328326

329327
// [Pointer] -> [Pointer]
330328
def GetPtrVirtBasePop : Opcode {

clang/test/AST/ByteCode/records.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1656,12 +1656,28 @@ namespace ExprWithCleanups {
16561656
static_assert(F == 1i, "");
16571657
}
16581658

1659-
namespace NullptrUpcast {
1659+
namespace NullptrCast {
16601660
struct A {};
16611661
struct B : A { int n; };
1662+
constexpr A *na = nullptr;
16621663
constexpr B *nb = nullptr;
16631664
constexpr A &ra = *nb; // both-error {{constant expression}} \
16641665
// both-note {{cannot access base class of null pointer}}
1666+
constexpr B &rb = (B&)*na; // both-error {{constant expression}} \
1667+
// both-note {{cannot access derived class of null pointer}}
1668+
constexpr bool test() {
1669+
auto a = (A*)(B*)nullptr;
1670+
1671+
return a == nullptr;
1672+
}
1673+
static_assert(test(), "");
1674+
1675+
constexpr bool test2() {
1676+
auto a = (B*)(A*)nullptr;
1677+
1678+
return a == nullptr;
1679+
}
1680+
static_assert(test2(), "");
16651681
}
16661682

16671683
namespace NonConst {

0 commit comments

Comments
 (0)