Skip to content

Commit

Permalink
support of pointer to non-static method
Browse files Browse the repository at this point in the history
Signed-off-by: Valentyn Yukhymenko <[email protected]>
  • Loading branch information
BaLiKfromUA committed Aug 29, 2024
1 parent 10fff74 commit 3c5d8f3
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 12 deletions.
59 changes: 50 additions & 9 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4491,19 +4491,51 @@ bool is_nonstatic_member_function(ValueDecl *FD) {

auto *MD = dyn_cast<CXXMethodDecl>(FD);
if (!MD) {
return false;
// might be a pointer to member function
QualType QT = FD->getType();
// check if the type is a pointer to a member
if (const MemberPointerType *MPT = QT->getAs<MemberPointerType>()) {
QualType PT = MPT->getPointeeType();
// check if the pointee type is a function type
if (const FunctionProtoType *FPT = PT->getAs<FunctionProtoType>()) {
return true;
}
}
} else {
return !MD->isStatic();
}

return !MD->isStatic();
return false;
}

bool is_method_in_class_or_parent(const CXXMethodDecl *MD,
const CXXRecordDecl *ObjClass) {
if (ObjClass == MD->getParent()) {
return true;
CXXMethodDecl *getCXXMethodDeclFromDeclRefExpr(DeclRefExpr *DRE) {
ValueDecl *VD = DRE->getDecl();

if (auto *MD = dyn_cast<CXXMethodDecl>(VD)) {
// method declaration
return MD;
} else {
// pointer to non-static method
// validation was done in is_nonstatic_member_function
auto *VarD = cast<VarDecl>(VD);
Expr *Init = VarD->getInit();

if (auto *UnaryOp = dyn_cast<UnaryOperator>(Init)) {
// get the operand of &
Init = UnaryOp->getSubExpr();
}

while (auto *CastExpr = dyn_cast<ImplicitCastExpr>(Init)) {
// handle ImplicitCastExpr if it is wrapping the actual expression
Init = CastExpr->getSubExpr();
}

if (auto *DREFromUnary = dyn_cast<DeclRefExpr>(Init)) {
return dyn_cast<CXXMethodDecl>(DREFromUnary->getDecl());
}
}

return ObjClass->isDerivedFrom(MD->getParent());
return nullptr;
}

bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator,
Expand Down Expand Up @@ -4730,10 +4762,19 @@ bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator,
<< Range;
}

auto *MD = cast<CXXMethodDecl>(DRE->getDecl());
CXXMethodDecl *MD = getCXXMethodDeclFromDeclRefExpr(DRE);
assert(MD && "failed to get method?");
// this call is needed to make
// CXXSpliceExpr work with pointers to non-static methods
// (we unwrap pointer in getCXXMethodDeclFromDeclRefExpr(DRE) function)
// for non-pointer setDecl(MD) call is no-op
DRE->setDecl(MD);

auto ObjClass = ObjType->getAsCXXRecordDecl();
// check that method belongs to class
if (!is_method_in_class_or_parent(MD, ObjType->getAsCXXRecordDecl())) {
bool IsMethodFromClassOrParent = (MD->getParent() == ObjClass) ||
ObjClass->isDerivedFrom(MD->getParent());
if (!IsMethodFromClassOrParent) {
return Diagnoser(Range.getBegin(),
diag::metafn_function_is_not_member_of_object)
<< Range;
Expand Down
19 changes: 16 additions & 3 deletions libcxx/test/std/experimental/reflection/reflect-invoke.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ constexpr int foo(int a) {
}

constexpr int (*foo_pointer)(int) = &foo;
static_assert(reflect_invoke(^^foo_pointer, {std::meta::reflect_value(1)})
== std::meta::reflect_value(43));
static_assert(reflect_invoke(^^foo_pointer, {std::meta::reflect_value(0)})
== std::meta::reflect_value(42));

// pointer to template function
template <typename T>
Expand All @@ -342,16 +342,29 @@ constexpr int (*bar_pointer)(int) = &bar<int>;
static_assert(reflect_invoke(^^bar_pointer, {std::meta::reflect_value(1)})
== std::meta::reflect_value(43));

// pointer to methods
// pointer to method
struct Cls {
public:
constexpr Cls(int data) : data(data) {}

static constexpr int fn(int p) { return p * p; }

constexpr int get() const { return data; }

const int data;
};

// pointer to static method
constexpr int (*fn_pointer)(int) = &Cls::fn;
static_assert(reflect_invoke(^^fn_pointer, {std::meta::reflect_value(2)})
== std::meta::reflect_value(4));

// pointer to non-static method
constexpr Cls data(42);
constexpr int (Cls::*get_pointer)() const = &Cls::get;
static_assert(reflect_invoke(^^get_pointer, {^^data}) == std::meta::reflect_value(42));


} // namespace function_pointer

int main() { }

0 comments on commit 3c5d8f3

Please sign in to comment.