Skip to content

[clang][Interp] Member Pointers #91303

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
Jun 6, 2024
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
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ add_clang_library(clangAST
Interp/Record.cpp
Interp/Source.cpp
Interp/State.cpp
Interp/MemberPointer.cpp
Interp/InterpShared.cpp
ItaniumCXXABI.cpp
ItaniumMangle.cpp
Expand Down
100 changes: 91 additions & 9 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,35 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitMemcpy(CE);
}

case CK_DerivedToBaseMemberPointer: {
assert(classifyPrim(CE->getType()) == PT_MemberPtr);
assert(classifyPrim(SubExpr->getType()) == PT_MemberPtr);
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();

unsigned DerivedOffset = collectBaseOffset(QualType(ToMP->getClass(), 0),
QualType(FromMP->getClass(), 0));

if (!this->visit(SubExpr))
return false;

return this->emitGetMemberPtrBasePop(DerivedOffset, CE);
}

case CK_BaseToDerivedMemberPointer: {
assert(classifyPrim(CE) == PT_MemberPtr);
assert(classifyPrim(SubExpr) == PT_MemberPtr);
const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
const auto *ToMP = CE->getType()->getAs<MemberPointerType>();

unsigned DerivedOffset = collectBaseOffset(QualType(FromMP->getClass(), 0),
QualType(ToMP->getClass(), 0));

if (!this->visit(SubExpr))
return false;
return this->emitGetMemberPtrBasePop(-DerivedOffset, CE);
}

case CK_UncheckedDerivedToBase:
case CK_DerivedToBase: {
if (!this->visit(SubExpr))
Expand Down Expand Up @@ -187,7 +216,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitCastFloatingIntegral(*ToT, CE);
}

case CK_NullToPointer: {
case CK_NullToPointer:
case CK_NullToMemberPointer: {
if (DiscardResult)
return true;

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

case CK_PointerToBoolean: {
case CK_PointerToBoolean:
case CK_MemberPointerToBoolean: {
PrimType PtrT = classifyPrim(SubExpr->getType());

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

if (BO->isPtrMemOp())
return this->visit(RHS);
if (BO->isPtrMemOp()) {
if (!this->visit(LHS))
return false;

if (!this->visit(RHS))
return false;

if (!this->emitToMemberPtr(BO))
return false;

if (classifyPrim(BO) == PT_MemberPtr)
return true;

if (!this->emitCastMemberPtrPtr(BO))
return false;
return DiscardResult ? this->emitPopPtr(BO) : true;
}

// Typecheck the args.
std::optional<PrimType> LT = classify(LHS->getType());
Expand Down Expand Up @@ -2773,6 +2819,8 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
return this->emitNullPtr(nullptr, E);
case PT_FnPtr:
return this->emitNullFnPtr(nullptr, E);
case PT_MemberPtr:
return this->emitNullMemberPtr(nullptr, E);
case PT_Float: {
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
}
Expand Down Expand Up @@ -2875,6 +2923,7 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
return this->emitConstBool(Value, E);
case PT_Ptr:
case PT_FnPtr:
case PT_MemberPtr:
case PT_Float:
case PT_IntAP:
case PT_IntAPS:
Expand Down Expand Up @@ -3308,10 +3357,27 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
}
}

std::optional<unsigned> CalleeOffset;
// Add the (optional, implicit) This pointer.
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
if (!this->visit(MC->getImplicitObjectArgument()))
if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
// If we end up creating a CallPtr op for this, we need the base of the
// member pointer as the instance pointer, and later extract the function
// decl as the function pointer.
const Expr *Callee = E->getCallee();
CalleeOffset =
this->allocateLocalPrimitive(Callee, PT_MemberPtr, true, false);
if (!this->visit(Callee))
return false;
if (!this->emitSetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetMemberPtrBase(E))
return false;
} else if (!this->visit(MC->getImplicitObjectArgument())) {
return false;
}
}

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

if (!this->visit(E->getCallee()))
return false;
// Get the callee, either from a member pointer saved in CalleeOffset,
// or by just visiting the Callee expr.
if (CalleeOffset) {
if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
return false;
if (!this->emitGetMemberPtrDecl(E))
return false;
if (!this->emitCallPtr(ArgSize, E, E))
return false;
} else {
if (!this->visit(E->getCallee()))
return false;

if (!this->emitCallPtr(ArgSize, E, E))
return false;
if (!this->emitCallPtr(ArgSize, E, E))
return false;
}
}

// Cleanup for discarded return values.
Expand Down Expand Up @@ -3623,6 +3700,11 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
return false;
return DiscardResult ? this->emitPop(*T, E) : true;
case UO_AddrOf: // &x
if (E->getType()->isMemberPointerType()) {
// C++11 [expr.unary.op]p3 has very strict rules on how the address of a
// member can be formed.
return this->emitGetMemberPtr(cast<DeclRefExpr>(SubExpr)->getDecl(), E);
}
// We should already have a pointer when we get here.
return this->delegate(SubExpr);
case UO_Deref: // *x
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isFloatingType())
return PT_Float;

if (T->isSpecificBuiltinType(BuiltinType::BoundMember) ||
T->isMemberPointerType())
return PT_MemberPtr;

if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember))
T->isFunctionType())
return PT_FnPtr;

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

if (const auto *DT = dyn_cast<MemberPointerType>(T))
return classify(DT->getPointeeType());

return std::nullopt;
}

Expand Down Expand Up @@ -292,10 +293,12 @@ unsigned Context::collectBaseOffset(const RecordDecl *BaseDecl,
}
if (CurDecl == FinalDecl)
break;

// break;
}

assert(OffsetSum > 0);
return OffsetSum;
}

const Record *Context::getRecord(const RecordDecl *D) const {
return P->getOrCreateRecord(D);
}
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class Context final {
unsigned collectBaseOffset(const RecordDecl *BaseDecl,
const RecordDecl *DerivedDecl) const;

const Record *getRecord(const RecordDecl *D) const;

private:
/// Runs a function.
bool Run(State &Parent, const Function *Func, APValue &Result);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "MemberPointer.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "Integral.h"
#include "IntegralAP.h"
#include "InterpFrame.h"
#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -122,6 +123,8 @@ static const char *primTypeToString(PrimType T) {
return "Ptr";
case PT_FnPtr:
return "FnPtr";
case PT_MemberPtr:
return "MemberPtr";
}
llvm_unreachable("Unhandled PrimType");
}
Expand Down
30 changes: 26 additions & 4 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,26 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}

bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
uint32_t Offset) {
uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
uint32_t PtrOffset = Ptr.getByteOffset();

// We subtract Offset from PtrOffset. The result must be at least
// MinOffset.
if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
return true;

const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
QualType TargetQT = E->getType()->getPointeeType();
QualType MostDerivedQT = Ptr.getDeclPtr().getType();

S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
<< MostDerivedQT << TargetQT;

return false;
}

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isConst())
Expand Down Expand Up @@ -493,10 +513,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!Ptr.isDummy()) {
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
}
return true;
}

Expand Down
Loading
Loading