Skip to content

Commit

Permalink
Implement is_structured_binding metafunction (llvm#58)
Browse files Browse the repository at this point in the history
* feat: add `is_structured_binding` to `meta` header

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* style: comment

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* test: add test and stub implementation

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* feat: initial implementation

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* test: add more test cases

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* style: formatting

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* revert: changes of `is_variable`

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* revert: few style changes

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* style: move code according to order from paper

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* style: move code according to order from paper

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* fix: fix edge case for `type_of` with tuple element from structured binding

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* test: structured binding with constant modifier

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* test: structured binding with volatile modifier

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* test: add more test cases of struct binding

Signed-off-by: Valentyn Yukhymenko <[email protected]>

* fix: support structured binding in `extract`function

Signed-off-by: Valentyn Yukhymenko <[email protected]>

---------

Signed-off-by: Valentyn Yukhymenko <[email protected]>
  • Loading branch information
BaLiKfromUA authored Jun 23, 2024
1 parent 9a070de commit 5157802
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 6 deletions.
32 changes: 28 additions & 4 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,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 @@ -415,6 +419,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 @@ -1571,7 +1576,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 @@ -2158,12 +2163,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 @@ -2198,6 +2203,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 @@ -3112,6 +3118,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 @@ -168,6 +168,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 @@ -440,6 +441,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 @@ -1044,6 +1046,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 @@ -463,6 +462,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

0 comments on commit 5157802

Please sign in to comment.