Skip to content

Commit 12a7897

Browse files
committed
[clang][Interp] BaseToDerived casts
We can implement these similarly to DerivedToBase casts. We just have to walk the class hierarchy, sum the base offsets and subtract it from the current base offset of the pointer. Differential Revision: https://reviews.llvm.org/D149133
1 parent f1246e9 commit 12a7897

File tree

7 files changed

+117
-12
lines changed

7 files changed

+117
-12
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,20 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
9292
if (!this->visit(SubExpr))
9393
return false;
9494

95-
return this->emitDerivedToBaseCasts(getRecordTy(SubExpr->getType()),
96-
getRecordTy(CE->getType()), CE);
95+
unsigned DerivedOffset = collectBaseOffset(getRecordTy(CE->getType()),
96+
getRecordTy(SubExpr->getType()));
97+
98+
return this->emitGetPtrBasePop(DerivedOffset, CE);
99+
}
100+
101+
case CK_BaseToDerived: {
102+
if (!this->visit(SubExpr))
103+
return false;
104+
105+
unsigned DerivedOffset = collectBaseOffset(getRecordTy(SubExpr->getType()),
106+
getRecordTy(CE->getType()));
107+
108+
return this->emitGetPtrDerivedPop(DerivedOffset, CE);
97109
}
98110

99111
case CK_FloatingCast: {
@@ -2262,35 +2274,34 @@ void ByteCodeExprGen<Emitter>::emitCleanup() {
22622274
}
22632275

22642276
template <class Emitter>
2265-
bool ByteCodeExprGen<Emitter>::emitDerivedToBaseCasts(
2266-
const RecordType *DerivedType, const RecordType *BaseType, const Expr *E) {
2267-
// Pointer of derived type is already on the stack.
2277+
unsigned
2278+
ByteCodeExprGen<Emitter>::collectBaseOffset(const RecordType *BaseType,
2279+
const RecordType *DerivedType) {
22682280
const auto *FinalDecl = cast<CXXRecordDecl>(BaseType->getDecl());
22692281
const RecordDecl *CurDecl = DerivedType->getDecl();
22702282
const Record *CurRecord = getRecord(CurDecl);
22712283
assert(CurDecl && FinalDecl);
2284+
2285+
unsigned OffsetSum = 0;
22722286
for (;;) {
22732287
assert(CurRecord->getNumBases() > 0);
22742288
// One level up
22752289
for (const Record::Base &B : CurRecord->bases()) {
22762290
const auto *BaseDecl = cast<CXXRecordDecl>(B.Decl);
22772291

22782292
if (BaseDecl == FinalDecl || BaseDecl->isDerivedFrom(FinalDecl)) {
2279-
// This decl will lead us to the final decl, so emit a base cast.
2280-
if (!this->emitGetPtrBasePop(B.Offset, E))
2281-
return false;
2282-
2293+
OffsetSum += B.Offset;
22832294
CurRecord = B.R;
22842295
CurDecl = BaseDecl;
22852296
break;
22862297
}
22872298
}
22882299
if (CurDecl == FinalDecl)
2289-
return true;
2300+
break;
22902301
}
22912302

2292-
llvm_unreachable("Couldn't find the base class?");
2293-
return false;
2303+
assert(OffsetSum > 0);
2304+
return OffsetSum;
22942305
}
22952306

22962307
/// When calling this, we have a pointer of the local-to-destroy

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
267267
bool emitRecordDestruction(const Descriptor *Desc);
268268
bool emitDerivedToBaseCasts(const RecordType *DerivedType,
269269
const RecordType *BaseType, const Expr *E);
270+
unsigned collectBaseOffset(const RecordType *BaseType,
271+
const RecordType *DerivedType);
270272

271273
protected:
272274
/// Variable to storage mapping.

clang/lib/AST/Interp/Interp.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,16 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
213213
return false;
214214
}
215215

216+
bool CheckBaseDerived(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
217+
CheckSubobjectKind CSK) {
218+
if (!Ptr.isOnePastEnd())
219+
return true;
220+
221+
const SourceInfo &Loc = S.Current->getSource(OpPC);
222+
S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
223+
return false;
224+
}
225+
216226
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
217227
assert(Ptr.isLive() && "Pointer is not live");
218228
if (!Ptr.isConst())

clang/lib/AST/Interp/Interp.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
6767
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
6868
CheckSubobjectKind CSK);
6969

70+
/// Checks if accessing a base or derived record of the given pointer is valid.
71+
bool CheckBaseDerived(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
72+
CheckSubobjectKind CSK);
73+
7074
/// Checks if a pointer points to const storage.
7175
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
7276

@@ -1157,10 +1161,22 @@ inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
11571161
return true;
11581162
}
11591163

1164+
inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
1165+
const Pointer &Ptr = S.Stk.pop<Pointer>();
1166+
if (!CheckNull(S, OpPC, Ptr, CSK_Derived))
1167+
return false;
1168+
if (!CheckBaseDerived(S, OpPC, Ptr, CSK_Derived))
1169+
return false;
1170+
S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
1171+
return true;
1172+
}
1173+
11601174
inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
11611175
const Pointer &Ptr = S.Stk.peek<Pointer>();
11621176
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
11631177
return false;
1178+
if (!CheckBaseDerived(S, OpPC, Ptr, CSK_Base))
1179+
return false;
11641180
S.Stk.push<Pointer>(Ptr.atField(Off));
11651181
return true;
11661182
}
@@ -1169,6 +1185,8 @@ inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
11691185
const Pointer &Ptr = S.Stk.pop<Pointer>();
11701186
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
11711187
return false;
1188+
if (!CheckBaseDerived(S, OpPC, Ptr, CSK_Base))
1189+
return false;
11721190
S.Stk.push<Pointer>(Ptr.atField(Off));
11731191
return true;
11741192
}

clang/lib/AST/Interp/Opcodes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@ def GetPtrBasePop : Opcode {
293293
let Args = [ArgUint32];
294294
}
295295

296+
def GetPtrDerivedPop : Opcode {
297+
let Args = [ArgUint32];
298+
}
299+
296300
// [Pointer] -> [Pointer]
297301
def GetPtrVirtBase : Opcode {
298302
// RecordDecl of base class.

clang/lib/AST/Interp/Pointer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ class Pointer {
109109
return Pointer(Pointee, Field, Field);
110110
}
111111

112+
/// Subtract the given offset from the current Base and Offset
113+
/// of the pointer.
114+
Pointer atFieldSub(unsigned Off) const {
115+
assert(Offset >= Off);
116+
unsigned O = Offset - Off;
117+
return Pointer(Pointee, O, O);
118+
}
119+
112120
/// Restricts the scope of an array element pointer.
113121
Pointer narrow() const {
114122
// Null pointers cannot be narrowed.

clang/test/AST/Interp/records.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,58 @@ namespace Destructors {
625625
// ref-note {{in call to 'testS()'}}
626626
}
627627

628+
namespace BaseToDerived {
629+
namespace A {
630+
struct A {};
631+
struct B : A { int n; };
632+
struct C : B {};
633+
C c = {};
634+
constexpr C *pb = (C*)((A*)&c + 1); // expected-error {{must be initialized by a constant expression}} \
635+
// expected-note {{cannot access derived class of pointer past the end of object}} \
636+
// ref-error {{must be initialized by a constant expression}} \
637+
// ref-note {{cannot access derived class of pointer past the end of object}}
638+
}
639+
namespace B {
640+
struct A {};
641+
struct Z {};
642+
struct B : Z, A {
643+
int n;
644+
constexpr B() : n(10) {}
645+
};
646+
struct C : B {
647+
constexpr C() : B() {}
648+
};
649+
650+
constexpr C c = {};
651+
constexpr const A *pa = &c;
652+
constexpr const C *cp = (C*)pa;
653+
constexpr const B *cb = (B*)cp;
654+
655+
static_assert(cb->n == 10);
656+
static_assert(cp->n == 10);
657+
}
658+
659+
namespace C {
660+
struct Base { int *a; };
661+
struct Base2 : Base { int f[12]; };
662+
663+
struct Middle1 { int b[3]; };
664+
struct Middle2 : Base2 { char c; };
665+
struct Middle3 : Middle2 { char g[3]; };
666+
struct Middle4 { int f[3]; };
667+
struct Middle5 : Middle4, Middle3 { char g2[3]; };
668+
669+
struct NotQuiteDerived : Middle1, Middle5 { bool d; };
670+
struct Derived : NotQuiteDerived { int e; };
671+
672+
constexpr NotQuiteDerived NQD1 = {};
673+
674+
constexpr Middle5 *M4 = (Middle5*)((Base2*)&NQD1);
675+
static_assert(M4->a == nullptr);
676+
static_assert(M4->g2[0] == 0);
677+
}
678+
}
679+
628680

629681
namespace VirtualDtors {
630682
class A {

0 commit comments

Comments
 (0)