Skip to content

Commit

Permalink
[clang][Interp] Handle PtrMemOps
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D144164
  • Loading branch information
tbaederr committed Jul 26, 2023
1 parent b2a2538 commit 8cb6e47
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 13 deletions.
9 changes: 6 additions & 3 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Expand Up @@ -207,13 +207,13 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
const Expr *LHS = BO->getLHS();
const Expr *RHS = BO->getRHS();

if (BO->isPtrMemOp())
return this->visit(RHS);

// Typecheck the args.
std::optional<PrimType> LT = classify(LHS->getType());
std::optional<PrimType> RT = classify(RHS->getType());
std::optional<PrimType> T = classify(BO->getType());
if (!LT || !RT || !T) {
return this->bail(BO);
}

auto Discard = [this, T, BO](bool Result) {
if (!Result)
Expand All @@ -228,6 +228,9 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
return Discard(this->visit(RHS));
}

if (!LT || !RT || !T)
return this->bail(BO);

// Pointer arithmetic special case.
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
if (T == PT_Ptr || (LT == PT_Ptr && RT == PT_Ptr))
Expand Down
19 changes: 13 additions & 6 deletions clang/lib/AST/Interp/Context.cpp
Expand Up @@ -88,12 +88,6 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }

std::optional<PrimType> Context::classify(QualType T) const {
if (T->isFunctionPointerType() || T->isFunctionReferenceType())
return PT_FnPtr;

if (T->isReferenceType() || T->isPointerType())
return PT_Ptr;

if (T->isBooleanType())
return PT_Bool;

Expand Down Expand Up @@ -133,9 +127,22 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isFloatingType())
return PT_Float;

if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
T->isFunctionType())
return PT_FnPtr;

if (T->isReferenceType() || T->isPointerType())
return PT_Ptr;

if (auto *AT = dyn_cast<AtomicType>(T))
return classify(AT->getValueType());

if (auto *DT = dyn_cast<DecltypeType>(T))
return classify(DT->getUnderlyingType());

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

return {};
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Expand Up @@ -1716,6 +1716,9 @@ inline bool CallPtr(InterpState &S, CodePtr OpPC) {
if (!F || !F->isConstexpr())
return false;

if (F->isVirtual())
return CallVirt(S, OpPC, F);

return Call(S, OpPC, F);
}

Expand Down
5 changes: 1 addition & 4 deletions clang/test/AST/Interp/literals.cpp
Expand Up @@ -149,13 +149,10 @@ namespace SizeOf {
// ref-error{{to a function type}}



/// FIXME: The following code should be accepted.
struct S {
void func();
};
constexpr void (S::*Func)() = &S::func; // expected-error {{must be initialized by a constant expression}} \
// expected-error {{interpreter failed to evaluate an expression}}
constexpr void (S::*Func)() = &S::func;
static_assert(sizeof(Func) == sizeof(&S::func), "");


Expand Down
47 changes: 47 additions & 0 deletions clang/test/AST/Interp/records.cpp
Expand Up @@ -689,5 +689,52 @@ namespace CtorDtor {
// ref-note {{initializer of 'D2' is not a constant expression}}

}

namespace VirtualFunctionPointers {
struct S {
virtual constexpr int func() const { return 1; }
};

struct Middle : S {
constexpr Middle(int i) : i(i) {}
int i;
};

struct Other {
constexpr Other(int k) : k(k) {}
int k;
};

struct S2 : Middle, Other {
int j;
constexpr S2(int i, int j, int k) : Middle(i), Other(k), j(j) {}
virtual constexpr int func() const { return i + j + k + S::func(); }
};

constexpr S s;
constexpr decltype(&S::func) foo = &S::func;
constexpr int value = (s.*foo)();
static_assert(value == 1);


constexpr S2 s2(1, 2, 3);
static_assert(s2.i == 1);
static_assert(s2.j == 2);
static_assert(s2.k == 3);

constexpr int value2 = s2.func();
constexpr int value3 = (s2.*foo)();
static_assert(value3 == 7);

constexpr int dynamicDispatch(const S &s) {
constexpr decltype(&S::func) SFunc = &S::func;

return (s.*SFunc)();
}

static_assert(dynamicDispatch(s) == 1);
static_assert(dynamicDispatch(s2) == 7);
};

};
#endif

0 comments on commit 8cb6e47

Please sign in to comment.