From 8cb6e476ccaa62b2621a5f68689a6070592f221d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Thu, 16 Feb 2023 08:37:03 +0100 Subject: [PATCH] [clang][Interp] Handle PtrMemOps Differential Revision: https://reviews.llvm.org/D144164 --- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 9 +++-- clang/lib/AST/Interp/Context.cpp | 19 +++++++--- clang/lib/AST/Interp/Interp.h | 3 ++ clang/test/AST/Interp/literals.cpp | 5 +-- clang/test/AST/Interp/records.cpp | 47 ++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 9f3eb158576fd..d2995fb57d8be 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -207,13 +207,13 @@ bool ByteCodeExprGen::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 LT = classify(LHS->getType()); std::optional RT = classify(RHS->getType()); std::optional T = classify(BO->getType()); - if (!LT || !RT || !T) { - return this->bail(BO); - } auto Discard = [this, T, BO](bool Result) { if (!Result) @@ -228,6 +228,9 @@ bool ByteCodeExprGen::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)) diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index eeb7fa9379f5c..2e5d721590edc 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -88,12 +88,6 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); } std::optional 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; @@ -133,9 +127,22 @@ std::optional 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(T)) return classify(AT->getValueType()); + if (auto *DT = dyn_cast(T)) + return classify(DT->getUnderlyingType()); + + if (auto *DT = dyn_cast(T)) + return classify(DT->getPointeeType()); + return {}; } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index ff67e873a0844..0acaf76435321 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -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); } diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index b9d1d2d59d11a..5df7b7e5e6e8b 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -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), ""); diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index 5fd423eff025b..fcdf75cfa9502 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -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