Skip to content

Commit a86c1e7

Browse files
authored
[clang][Interp] Member Pointers (#91303)
This adds a `MemberPointer` class along with a `PT_MemberPtr` primitive type. A `MemberPointer` has a `Pointer` Base as well as a `Decl*` (could be `ValueDecl*`?) decl it points to. For the actual logic, this mainly changes the way we handle `PtrMemOp`s in `VisitBinaryOperator`.
1 parent bf02f81 commit a86c1e7

25 files changed

+705
-25
lines changed

clang/lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ add_clang_library(clangAST
8787
Interp/Record.cpp
8888
Interp/Source.cpp
8989
Interp/State.cpp
90+
Interp/MemberPointer.cpp
9091
Interp/InterpShared.cpp
9192
ItaniumCXXABI.cpp
9293
ItaniumMangle.cpp

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,35 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
100100
return this->emitMemcpy(CE);
101101
}
102102

103+
case CK_DerivedToBaseMemberPointer: {
104+
assert(classifyPrim(CE->getType()) == PT_MemberPtr);
105+
assert(classifyPrim(SubExpr->getType()) == PT_MemberPtr);
106+
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
107+
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();
108+
109+
unsigned DerivedOffset = collectBaseOffset(QualType(ToMP->getClass(), 0),
110+
QualType(FromMP->getClass(), 0));
111+
112+
if (!this->visit(SubExpr))
113+
return false;
114+
115+
return this->emitGetMemberPtrBasePop(DerivedOffset, CE);
116+
}
117+
118+
case CK_BaseToDerivedMemberPointer: {
119+
assert(classifyPrim(CE) == PT_MemberPtr);
120+
assert(classifyPrim(SubExpr) == PT_MemberPtr);
121+
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
122+
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();
123+
124+
unsigned DerivedOffset = collectBaseOffset(QualType(FromMP->getClass(), 0),
125+
QualType(ToMP->getClass(), 0));
126+
127+
if (!this->visit(SubExpr))
128+
return false;
129+
return this->emitGetMemberPtrBasePop(-DerivedOffset, CE);
130+
}
131+
103132
case CK_UncheckedDerivedToBase:
104133
case CK_DerivedToBase: {
105134
if (!this->visit(SubExpr))
@@ -187,7 +216,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
187216
return this->emitCastFloatingIntegral(*ToT, CE);
188217
}
189218

190-
case CK_NullToPointer: {
219+
case CK_NullToPointer:
220+
case CK_NullToMemberPointer: {
191221
if (DiscardResult)
192222
return true;
193223

@@ -326,7 +356,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
326356
return this->emitCast(*FromT, *ToT, CE);
327357
}
328358

329-
case CK_PointerToBoolean: {
359+
case CK_PointerToBoolean:
360+
case CK_MemberPointerToBoolean: {
330361
PrimType PtrT = classifyPrim(SubExpr->getType());
331362

332363
// Just emit p != nullptr for this.
@@ -534,8 +565,23 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
534565
BO->isComparisonOp())
535566
return this->emitComplexComparison(LHS, RHS, BO);
536567

537-
if (BO->isPtrMemOp())
538-
return this->visit(RHS);
568+
if (BO->isPtrMemOp()) {
569+
if (!this->visit(LHS))
570+
return false;
571+
572+
if (!this->visit(RHS))
573+
return false;
574+
575+
if (!this->emitToMemberPtr(BO))
576+
return false;
577+
578+
if (classifyPrim(BO) == PT_MemberPtr)
579+
return true;
580+
581+
if (!this->emitCastMemberPtrPtr(BO))
582+
return false;
583+
return DiscardResult ? this->emitPopPtr(BO) : true;
584+
}
539585

540586
// Typecheck the args.
541587
std::optional<PrimType> LT = classify(LHS->getType());
@@ -2773,6 +2819,8 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
27732819
return this->emitNullPtr(nullptr, E);
27742820
case PT_FnPtr:
27752821
return this->emitNullFnPtr(nullptr, E);
2822+
case PT_MemberPtr:
2823+
return this->emitNullMemberPtr(nullptr, E);
27762824
case PT_Float: {
27772825
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
27782826
}
@@ -2875,6 +2923,7 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
28752923
return this->emitConstBool(Value, E);
28762924
case PT_Ptr:
28772925
case PT_FnPtr:
2926+
case PT_MemberPtr:
28782927
case PT_Float:
28792928
case PT_IntAP:
28802929
case PT_IntAPS:
@@ -3308,10 +3357,27 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
33083357
}
33093358
}
33103359

3360+
std::optional<unsigned> CalleeOffset;
33113361
// Add the (optional, implicit) This pointer.
33123362
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
3313-
if (!this->visit(MC->getImplicitObjectArgument()))
3363+
if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
3364+
// If we end up creating a CallPtr op for this, we need the base of the
3365+
// member pointer as the instance pointer, and later extract the function
3366+
// decl as the function pointer.
3367+
const Expr *Callee = E->getCallee();
3368+
CalleeOffset =
3369+
this->allocateLocalPrimitive(Callee, PT_MemberPtr, true, false);
3370+
if (!this->visit(Callee))
3371+
return false;
3372+
if (!this->emitSetLocal(PT_MemberPtr, *CalleeOffset, E))
3373+
return false;
3374+
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
3375+
return false;
3376+
if (!this->emitGetMemberPtrBase(E))
3377+
return false;
3378+
} else if (!this->visit(MC->getImplicitObjectArgument())) {
33143379
return false;
3380+
}
33153381
}
33163382

33173383
llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
@@ -3380,11 +3446,22 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
33803446
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
33813447
ArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));
33823448

3383-
if (!this->visit(E->getCallee()))
3384-
return false;
3449+
// Get the callee, either from a member pointer saved in CalleeOffset,
3450+
// or by just visiting the Callee expr.
3451+
if (CalleeOffset) {
3452+
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
3453+
return false;
3454+
if (!this->emitGetMemberPtrDecl(E))
3455+
return false;
3456+
if (!this->emitCallPtr(ArgSize, E, E))
3457+
return false;
3458+
} else {
3459+
if (!this->visit(E->getCallee()))
3460+
return false;
33853461

3386-
if (!this->emitCallPtr(ArgSize, E, E))
3387-
return false;
3462+
if (!this->emitCallPtr(ArgSize, E, E))
3463+
return false;
3464+
}
33883465
}
33893466

33903467
// Cleanup for discarded return values.
@@ -3623,6 +3700,11 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
36233700
return false;
36243701
return DiscardResult ? this->emitPop(*T, E) : true;
36253702
case UO_AddrOf: // &x
3703+
if (E->getType()->isMemberPointerType()) {
3704+
// C++11 [expr.unary.op]p3 has very strict rules on how the address of a
3705+
// member can be formed.
3706+
return this->emitGetMemberPtr(cast<DeclRefExpr>(SubExpr)->getDecl(), E);
3707+
}
36263708
// We should already have a pointer when we get here.
36273709
return this->delegate(SubExpr);
36283710
case UO_Deref: // *x

clang/lib/AST/Interp/Context.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,12 @@ std::optional<PrimType> Context::classify(QualType T) const {
163163
if (T->isFloatingType())
164164
return PT_Float;
165165

166+
if (T->isSpecificBuiltinType(BuiltinType::BoundMember) ||
167+
T->isMemberPointerType())
168+
return PT_MemberPtr;
169+
166170
if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
167-
T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember))
171+
T->isFunctionType())
168172
return PT_FnPtr;
169173

170174
if (T->isReferenceType() || T->isPointerType() ||
@@ -177,9 +181,6 @@ std::optional<PrimType> Context::classify(QualType T) const {
177181
if (const auto *DT = dyn_cast<DecltypeType>(T))
178182
return classify(DT->getUnderlyingType());
179183

180-
if (const auto *DT = dyn_cast<MemberPointerType>(T))
181-
return classify(DT->getPointeeType());
182-
183184
return std::nullopt;
184185
}
185186

@@ -292,10 +293,12 @@ unsigned Context::collectBaseOffset(const RecordDecl *BaseDecl,
292293
}
293294
if (CurDecl == FinalDecl)
294295
break;
295-
296-
// break;
297296
}
298297

299298
assert(OffsetSum > 0);
300299
return OffsetSum;
301300
}
301+
302+
const Record *Context::getRecord(const RecordDecl *D) const {
303+
return P->getOrCreateRecord(D);
304+
}

clang/lib/AST/Interp/Context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ class Context final {
107107
unsigned collectBaseOffset(const RecordDecl *BaseDecl,
108108
const RecordDecl *DerivedDecl) const;
109109

110+
const Record *getRecord(const RecordDecl *D) const;
111+
110112
private:
111113
/// Runs a function.
112114
bool Run(State &Parent, const Function *Func, APValue &Result);

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Floating.h"
1212
#include "FunctionPointer.h"
1313
#include "IntegralAP.h"
14+
#include "MemberPointer.h"
1415
#include "Pointer.h"
1516
#include "PrimType.h"
1617
#include "Record.h"

clang/lib/AST/Interp/Disasm.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "Integral.h"
2020
#include "IntegralAP.h"
2121
#include "InterpFrame.h"
22+
#include "MemberPointer.h"
2223
#include "Opcode.h"
2324
#include "PrimType.h"
2425
#include "Program.h"
@@ -122,6 +123,8 @@ static const char *primTypeToString(PrimType T) {
122123
return "Ptr";
123124
case PT_FnPtr:
124125
return "FnPtr";
126+
case PT_MemberPtr:
127+
return "MemberPtr";
125128
}
126129
llvm_unreachable("Unhandled PrimType");
127130
}

clang/lib/AST/Interp/Interp.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,26 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
373373
return false;
374374
}
375375

376+
bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
377+
uint32_t Offset) {
378+
uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
379+
uint32_t PtrOffset = Ptr.getByteOffset();
380+
381+
// We subtract Offset from PtrOffset. The result must be at least
382+
// MinOffset.
383+
if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
384+
return true;
385+
386+
const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
387+
QualType TargetQT = E->getType()->getPointeeType();
388+
QualType MostDerivedQT = Ptr.getDeclPtr().getType();
389+
390+
S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
391+
<< MostDerivedQT << TargetQT;
392+
393+
return false;
394+
}
395+
376396
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
377397
assert(Ptr.isLive() && "Pointer is not live");
378398
if (!Ptr.isConst())
@@ -493,10 +513,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
493513
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
494514
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
495515
return false;
496-
if (!CheckExtern(S, OpPC, Ptr))
497-
return false;
498-
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
499-
return false;
516+
if (!Ptr.isDummy()) {
517+
if (!CheckExtern(S, OpPC, Ptr))
518+
return false;
519+
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
520+
return false;
521+
}
500522
return true;
501523
}
502524

0 commit comments

Comments
 (0)