Skip to content
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

Use reflect_invoke metafunction to invoke a member function #79

Merged
merged 24 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d2b3a59
initial implementation
BaLiKfromUA Jul 23, 2024
cda6611
Merge branch 'p2996' into clang-p2996/issues/5-refined
BaLiKfromUA Jul 31, 2024
618dcf4
format
BaLiKfromUA Jul 31, 2024
cd8adb6
refactoring to simplify if statements
BaLiKfromUA Aug 1, 2024
e7899d2
support of template member functions
BaLiKfromUA Aug 1, 2024
dc8003c
add diagnostic
BaLiKfromUA Aug 2, 2024
81a4dd7
wording
BaLiKfromUA Aug 2, 2024
a8b7a4a
more optimal exclusion of object argument
BaLiKfromUA Aug 9, 2024
8d32747
hack with wrapping decl reference into splice expr to prevent lookup …
BaLiKfromUA Aug 12, 2024
6479e09
fix bug + add automated tests for diagnostics
BaLiKfromUA Aug 13, 2024
16e63c7
comment
BaLiKfromUA Aug 13, 2024
22a2108
clean up
BaLiKfromUA Aug 21, 2024
c416cfb
test refactoring
BaLiKfromUA Aug 21, 2024
2dd9246
apply different small review remarks
BaLiKfromUA Aug 23, 2024
fe85f72
add support for using methods of base classes
BaLiKfromUA Aug 23, 2024
ff458eb
Merge branch 'p2996' into clang-p2996/issues/5-refined
BaLiKfromUA Aug 26, 2024
7a47147
migrate existing tests to new syntax
BaLiKfromUA Aug 27, 2024
10fff74
test existing functionality related to function pointers
BaLiKfromUA Aug 27, 2024
3c5d8f3
support of pointer to non-static method
BaLiKfromUA Aug 29, 2024
d95299b
test for object with static storage duration holding a pointer to a c…
BaLiKfromUA Aug 29, 2024
4974bfa
address misunderstanding about static pointers + fix bug related to it
BaLiKfromUA Sep 4, 2024
3bab1b1
comments
BaLiKfromUA Sep 4, 2024
562137e
add requested tests related to template functions -- passed
BaLiKfromUA Sep 16, 2024
b4ec538
refactor logic of getting CXXMethodDecl from function pointer
BaLiKfromUA Sep 16, 2024
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
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticMetafnKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ def metafn_returns_non_structural_type : Note<
"represented as a reflection">;
def metafn_invocation_not_constant_expr : Note<
"invocation is not a constant expression">;
def metafn_first_argument_is_not_object : Note<
"expected related object reflection as a first argument for invoking "
"non-static member function">;
def metafn_function_is_not_member_of_object : Note<
"method is not a member of "
"given object reflection">;
def metafn_function_returns_void : Note<
"cannot invoke reflection of void function">;


// Extraction.
def metafn_cannot_extract : Note<
Expand Down
114 changes: 102 additions & 12 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4420,6 +4420,25 @@ bool reflect_result(APValue &Result, Sema &S, EvalFn Evaluator,
return SetAndSucceed(Result, APValue(RV));
}

bool is_nonstatic_member_function(ValueDecl *FD) {
if (!FD) {
return false;
}

if (dyn_cast<CXXConstructorDecl>(FD)) {
return false;
}

// todo: check that method is not special??

auto *MD = dyn_cast<CXXMethodDecl>(FD);
if (!MD) {
return false;
}

return !MD->isStatic();
}

bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator,
DiagFn Diagnoser, QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args) {
Expand Down Expand Up @@ -4572,10 +4591,18 @@ bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator,
{
sema::TemplateDeductionInfo Info(Args[0]->getExprLoc(),
FTD->getTemplateDepth());

bool exclude_first_arg =
is_nonstatic_member_function(FTD->getTemplatedDecl()) &&
ArgExprs.size() > 0;

TemplateDeductionResult Result = S.DeduceTemplateArguments(
FTD, &ExplicitTAListInfo, ArgExprs, Specialization, Info, false,
true, QualType(), Expr::Classification(),
[](ArrayRef<QualType>) { return false; });
FTD, &ExplicitTAListInfo,
exclude_first_arg
? SmallVector<Expr *, 4>(ArgExprs.begin() + 1, ArgExprs.end())
: ArgExprs,
Specialization, Info, false, true, QualType(), Expr::Classification(),
[](ArrayRef<QualType>) { return false; });
if (Result != TemplateDeductionResult::Success)
return Diagnoser(Range.getBegin(), diag::metafn_no_specialization_found)
<< FTD << Range;
Expand All @@ -4595,20 +4622,83 @@ bool reflect_invoke(APValue &Result, Sema &S, EvalFn Evaluator,
ExprResult ER;
{
EnterExpressionEvaluationContext Context(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
if (auto *DRE = dyn_cast<DeclRefExpr>(FnRefExpr);
DRE && dyn_cast<CXXConstructorDecl>(DRE->getDecl())) {
auto *CtorD = cast<CXXConstructorDecl>(DRE->getDecl());
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);

auto *DRE = dyn_cast<DeclRefExpr>(FnRefExpr);
if (DRE && dyn_cast<CXXConstructorDecl>(DRE->getDecl())) {
auto *CtorD = cast<CXXConstructorDecl>(DRE->getDecl());
ER = S.BuildCXXConstructExpr(
Range.getBegin(), QualType(CtorD->getParent()->getTypeForDecl(), 0),
CtorD, false, ArgExprs, false, false, false, false,
CXXConstructionKind::Complete, Range);
Range.getBegin(), QualType(CtorD->getParent()->getTypeForDecl(), 0),
CtorD, false, ArgExprs, false, false, false, false,
CXXConstructionKind::Complete, Range);
} else {
ER = S.ActOnCallExpr(S.getCurScope(), FnRefExpr, Range.getBegin(),
ArgExprs, Range.getEnd(), /*ExecConfig=*/nullptr);
auto *FnExpr = FnRefExpr;
auto FnArgExprs = ArgExprs;

if (DRE && is_nonstatic_member_function(DRE->getDecl())) {
auto *MD = cast<CXXMethodDecl>(DRE->getDecl());

if (ArgExprs.size() < 1) {
// need to have object as a first argument
return Diagnoser(Range.getBegin(),
diag::metafn_first_argument_is_not_object)
<< Range;
}

auto ObjExpr = ArgExprs[0];
auto ObjType = ObjExpr->getType();

if (ObjType->isPointerType()) {
ObjType = ObjType->getPointeeType();
}

if (!ObjType->getAsCXXRecordDecl()) {
// first argument is not an object
return Diagnoser(Range.getBegin(),
diag::metafn_first_argument_is_not_object)
<< Range;
}

// check that method belongs to class
if (ObjType->getAsCXXRecordDecl()->getDeclName() !=
MD->getParent()->getDeclName()) {
return Diagnoser(Range.getBegin(),
diag::metafn_function_is_not_member_of_object)
<< Range;
}

if (MD->getReturnType()->isVoidType()) {
// void return type is not supported
return Diagnoser(Range.getBegin(), diag::metafn_function_returns_void)
<< Range;
}

SourceLocation ObjLoc = ObjExpr->getExprLoc();
UnqualifiedId Name;
Name.setIdentifier(MD->getIdentifier(), ObjLoc);

CXXScopeSpec SS;
SourceLocation TemplateKWLoc;

ExprResult MemberAccessResult = S.ActOnMemberAccessExpr(
S.getCurScope(), S.MakeFullExpr(ObjExpr).get(), ObjLoc,
ObjExpr->getType()->isPointerType() ? tok::arrow : tok::period, SS,
TemplateKWLoc, Name, nullptr);

if (MemberAccessResult.isInvalid()) {
return true;
}

FnExpr = MemberAccessResult.get();
// exclude first argument because it's an object
FnArgExprs = {ArgExprs.begin() + 1, ArgExprs.end()};
}

ER = S.ActOnCallExpr(S.getCurScope(), FnExpr, Range.getBegin(),
FnArgExprs, Range.getEnd(), /*ExecConfig=*/nullptr);
}
}

if (ER.isInvalid())
return Diagnoser(Range.getBegin(), diag::metafn_invalid_call_expr) << Range;
Expr *ResultExpr = ER.get();
Expand Down
42 changes: 40 additions & 2 deletions libcxx/test/std/experimental/reflection/reflect-invoke.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ static_assert([:reflect_invoke(^Cls::fn, {std::meta::reflect_value(4)}):] == 16)
// With reflection of constexpr variable as an argument.
static constexpr int five = 5;
static_assert([:reflect_invoke(^fn1, {^five}):] == 47);

// TODO(P2996): Support nonstatic member functions.
} // namespace basic_functions

// =================
Expand Down Expand Up @@ -241,4 +239,44 @@ static_assert(
std::views::transform(std::meta::reflect_value<int>)));
} // namespace with_non_contiguous_ranges

namespace non_static_member_functions {

class Number {
public:
constexpr Number(int v) : value(v) {}

consteval int plus(int a) const { return plus_impl(a); }

consteval int get_value() const { return value; }

template <typename T>
consteval T multiply(T x) const {
return value * x;
}

private:
consteval int plus_impl(int a) const {
return value + a;
}

const int value;
};

constexpr Number num{42};
constexpr auto num_ref = &num;

static_assert(std::meta::reflect_value(84) ==
reflect_invoke(^Number::plus,
{^num, std::meta::reflect_value(42)}));
static_assert(std::meta::reflect_value(42) ==
reflect_invoke(^Number::get_value, {^num}));

static_assert(std::meta::reflect_value(42) ==
reflect_invoke(^Number::get_value, {^num_ref}));

static_assert(std::meta::reflect_value(84) ==
reflect_invoke(^Number::multiply, {^int},
{^num, std::meta::reflect_value(2)}));
} // namespace non_static_member_functions

int main() { }
Loading