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

Implement is_structured_binding metafunction #58

Merged
merged 16 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 28 additions & 4 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ static bool is_concept(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);

static bool is_structured_binding(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);

static bool is_value(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);
Expand Down Expand Up @@ -410,6 +414,7 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_bool, 1, 1, is_class_template },
{ Metafunction::MFRK_bool, 1, 1, is_alias_template },
{ Metafunction::MFRK_bool, 1, 1, is_concept },
{ Metafunction::MFRK_bool, 1, 1, is_structured_binding },
{ Metafunction::MFRK_bool, 1, 1, is_value },
{ Metafunction::MFRK_bool, 1, 1, is_object },
{ Metafunction::MFRK_bool, 1, 1, has_template_arguments },
Expand Down Expand Up @@ -1553,7 +1558,7 @@ bool type_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
case ReflectionValue::RK_declaration: {
ValueDecl *VD = cast<ValueDecl>(R.getReflectedDecl());

bool UnwrapAliases = isa<ParmVarDecl>(VD);
bool UnwrapAliases = isa<ParmVarDecl>(VD) || isa<BindingDecl>(VD);
bool DropCV = isa<ParmVarDecl>(VD);
QualType QT = desugarType(VD->getType(), UnwrapAliases, DropCV,
/*DropRefs=*/false);
Expand Down Expand Up @@ -2140,12 +2145,12 @@ bool extract(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
Synthesized = ExtractLValueExpr::Create(S.Context, Range, ResultTy,
Decl);
}
} else if (ReturnsLValue) {
// Only variables may be returned as LValues.
} else if (ReturnsLValue && !isa<BindingDecl>(Decl)) {
// Only variables and structured binding may be returned as LValues.
return true;
} else {
// We have a reflection of a non-variable entity (either a field,
// function, enumerator, or lambda).
// function, enumerator, structured binding, or lambda).
NestedNameSpecifierLocBuilder NNSLocBuilder;
if (auto *ParentClsDecl = dyn_cast_or_null<CXXRecordDecl>(
Decl->getDeclContext())) {
Expand Down Expand Up @@ -2180,6 +2185,7 @@ bool extract(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
Range.getEnd(), Synthesized);
if (ER.isInvalid())
return true;

Synthesized = ER.get();
}
}
Expand Down Expand Up @@ -3062,6 +3068,24 @@ bool is_concept(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
return SetAndSucceed(Result, makeBool(S.Context, IsConcept));
}

bool is_structured_binding(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args) {
assert(Args[0]->getType()->isReflectionType());
assert(ResultTy == S.Context.BoolTy);

APValue R;
if (!Evaluator(R, Args[0], true))
return true;

bool result = false;
if (R.getReflection().getKind() == ReflectionValue::RK_declaration) {
result = isa<const BindingDecl>(R.getReflectedDecl());
}

return SetAndSucceed(Result, makeBool(S.Context, result));
}

bool is_value(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
SourceRange Range, ArrayRef<Expr *> Args) {
assert(Args[0]->getType()->isReflectionType());
Expand Down
7 changes: 7 additions & 0 deletions libcxx/include/experimental/meta
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ consteval auto is_variable_template(info) -> bool;
consteval auto is_class_template(bool) -> bool;
consteval auto is_alias_template(bool) -> bool;
consteval auto is_concept(info) -> bool;
consteval auto is_structured_binding(info) -> bool;
consteval auto has_template_arguments(info) -> bool;
consteval auto is_constructor(info) -> bool;
consteval auto is_destructor(info) -> bool;
Expand Down Expand Up @@ -438,6 +439,7 @@ enum : unsigned {
__metafn_is_class_template,
__metafn_is_alias_template,
__metafn_is_concept,
__metafn_is_structured_binding,
__metafn_is_value,
__metafn_is_object,
__metafn_has_template_arguments,
Expand Down Expand Up @@ -1037,6 +1039,11 @@ consteval auto is_concept(info r) -> bool {
return __metafunction(detail::__metafn_is_concept, r);
}

// Returns whether the reflected entity is a structured binding.
consteval auto is_structured_binding(info r) -> bool {
return __metafunction(detail::__metafn_is_structured_binding, r);
}

// Returns true if the reflected entity is a value.
consteval auto is_value(info r) -> bool {
return __metafunction(detail::__metafn_is_value, r);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
//
// [reflection]


#include <experimental/meta>

#include <tuple>

struct type {};
using alias = type;
Expand Down Expand Up @@ -446,6 +445,81 @@ static_assert(!is_base(^Base));
static_assert(!is_type(bases_of(^Derived)[0]));
static_assert(is_base(bases_of(^Derived)[0]));

// =====================
// test_is_structured_binding_and_related_edge_cases
// =====================
namespace test_is_structured_binding_and_related_edge_cases {
static int struct_binding_case1[] = {1, 2, 3};
auto [x1, y1, z1] = struct_binding_case1;
static_assert(is_structured_binding(^x1));
static_assert(is_structured_binding(^y1));
static_assert(is_structured_binding(^z1));
static_assert(!is_variable(^x1));
static_assert(!is_variable(^y1));
static_assert(!is_variable(^z1));
static_assert(type_of(^x1) == ^int);
static_assert(type_of(^y1) == ^int);
static_assert(type_of(^z1) == ^int);

auto struct_binding_case2() { return std::make_tuple(1, 2, 3); }
auto [x2, y2, z2] = struct_binding_case2();
static_assert(is_structured_binding(^x2));
static_assert(is_structured_binding(^y2));
static_assert(is_structured_binding(^z2));
static_assert(!is_variable(^x2));
static_assert(!is_variable(^y2));
static_assert(!is_variable(^z2));
// "wrapped" type of each element is 'std::tuple_element<I, std::tuple<int,int,int>>::type',
// where I is index of tuple field
static_assert(type_of(^x2) == ^int);
static_assert(type_of(^y2) == ^int);
static_assert(type_of(^z2) == ^int);

struct StructBinding {
const int a;
int b;
volatile double c;
};
auto struct_binding_case3() { return StructBinding{1, 2, 3.14}; }
auto [x3, y3, z3] = struct_binding_case3();
static_assert(is_structured_binding(^x3));
static_assert(is_structured_binding(^y3));
static_assert(is_structured_binding(^z3));
static_assert(!is_variable(^x3));
static_assert(!is_variable(^y3));
static_assert(!is_variable(^z3));
static_assert(type_of(^x3) == ^const int);
static_assert(type_of(^y3) == ^int);
static_assert(type_of(^z3) == ^volatile double);

constexpr auto p = std::pair{1, 2};
auto& [x4, y4] = p;
static_assert(is_structured_binding(^x4));
static_assert(is_structured_binding(^y4));
static_assert(!is_variable(^x4));
static_assert(!is_variable(^y4));
static_assert(type_of(^x4) == ^const int);
static_assert(type_of(^y4) == ^const int);
static_assert(extract<int>(^x4) == x4);
static_assert(extract<int&>(^x4) == x4);
static_assert(&extract<int&>(^x4) == &x4);
static_assert(extract<int>(^y4) == y4);
static_assert(extract<int&>(^y4) == y4);
static_assert(&extract<int&>(^y4) == &y4);

int a = 1, b = 2;
const auto& [x5, y5] = std::tie(a, b); // x5 and y5 are of type int&
static_assert(is_structured_binding(^x5));
static_assert(is_structured_binding(^y5));
static_assert(!is_variable(^x5));
static_assert(!is_variable(^y5));
static_assert(type_of(^x5) == ^int&);
static_assert(type_of(^y5) == ^int&);

static_assert(!is_structured_binding(^var));
static_assert(!is_structured_binding(std::meta::reflect_value(3)));
} // namespace test_is_structured_binding_and_related_edge_cases

// =====================
// test_is_user_provided
// =====================
Expand Down