diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -207,13 +207,13 @@ 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 @@ 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 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -88,12 +88,6 @@ 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 @@ 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 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1716,6 +1716,9 @@ 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 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -149,13 +149,10 @@ // 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 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -689,5 +689,52 @@ // 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