Skip to content

Commit

Permalink
Add 'is_const' and 'is_volatile' metafunctions.
Browse files Browse the repository at this point in the history
Closes issues llvm#68 and llvm#69.
  • Loading branch information
katzdm committed Jul 2, 2024
1 parent bfeb3a5 commit c683e64
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 3 deletions.
103 changes: 100 additions & 3 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ static bool is_bit_field(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);

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

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

static bool is_lvalue_reference_qualified(APValue &Result, Sema &S,
EvalFn Evaluator, QualType ResultTy,
SourceRange Range,
Expand Down Expand Up @@ -441,6 +449,8 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_bool, 1, 1, is_explicit },
{ Metafunction::MFRK_bool, 1, 1, is_noexcept },
{ Metafunction::MFRK_bool, 1, 1, is_bit_field },
{ Metafunction::MFRK_bool, 1, 1, is_const },
{ Metafunction::MFRK_bool, 1, 1, is_volatile },
{ Metafunction::MFRK_bool, 1, 1, is_lvalue_reference_qualified },
{ Metafunction::MFRK_bool, 1, 1, is_rvalue_reference_qualified },
{ Metafunction::MFRK_bool, 1, 1, has_static_storage_duration },
Expand Down Expand Up @@ -597,9 +607,9 @@ static void encodeName(std::string &Result, StringRef In, bool BasicOnly) {
Result = In;
} else {
Result.reserve(In.size() * 8); // more than enough for '\u{XYZ}'

llvm::raw_string_ostream OS(Result);

const char *InCursor = In.begin();
while (InCursor < In.end()) {
if (isBasicCharacter(*InCursor)) {
Expand Down Expand Up @@ -1175,6 +1185,22 @@ static bool isFunctionOrMethodNoexcept(const QualType QT) {
return false;
}

static bool isConstQualifiedType(QualType QT) {
bool result = QT.isConstQualified();
if (auto *FPT = dyn_cast<FunctionProtoType>(QT))
result |= FPT->isConst();

return result;
}

static bool isVolatileQualifiedType(QualType QT) {
bool result = QT.isVolatileQualified();
if (auto *FPT = dyn_cast<FunctionProtoType>(QT))
result |= FPT->isVolatile();

return result;
}

// -----------------------------------------------------------------------------
// Metafunction implementations
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -2747,7 +2773,7 @@ bool is_noexcept(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
case ReflectionValue::RK_declaration: {
const ValueDecl *D = R.getReflectedDecl();
const auto result = isFunctionOrMethodNoexcept(D->getType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
}
Expand All @@ -2771,6 +2797,77 @@ bool is_bit_field(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
return SetAndSucceed(Result, makeBool(S.Context, result));
}

bool is_const(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;

switch (R.getReflection().getKind()) {
case ReflectionValue::RK_null:
case ReflectionValue::RK_template:
case ReflectionValue::RK_namespace:
case ReflectionValue::RK_base_specifier:
case ReflectionValue::RK_data_member_spec:
return SetAndSucceed(Result, makeBool(S.Context, false));
case ReflectionValue::RK_type: {
bool result = isConstQualifiedType(R.getReflectedType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
case ReflectionValue::RK_declaration: {
bool result = isConstQualifiedType(R.getReflectedDecl()->getType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
case ReflectionValue::RK_expr_result: {
bool result = isConstQualifiedType(R.getReflectedExprResult()->getType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
}
llvm_unreachable("invalid reflection type");
}

bool is_volatile(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;

switch (R.getReflection().getKind()) {
case ReflectionValue::RK_null:
case ReflectionValue::RK_template:
case ReflectionValue::RK_namespace:
case ReflectionValue::RK_base_specifier:
case ReflectionValue::RK_data_member_spec:
return SetAndSucceed(Result, makeBool(S.Context, false));
case ReflectionValue::RK_type: {
bool result = isVolatileQualifiedType(R.getReflectedType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
case ReflectionValue::RK_declaration: {
bool result = isVolatileQualifiedType(R.getReflectedDecl()->getType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
case ReflectionValue::RK_expr_result: {
bool result =
isVolatileQualifiedType(R.getReflectedExprResult()->getType());

return SetAndSucceed(Result, makeBool(S.Context, result));
}
}
llvm_unreachable("invalid reflection type");
}

bool is_lvalue_reference_qualified(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args) {
Expand Down
16 changes: 16 additions & 0 deletions libcxx/include/experimental/meta
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ consteval auto is_defaulted(info) -> bool;
consteval auto is_explicit(info) -> bool;
consteval bool is_noexcept(info) -> bool;
consteval auto is_bit_field(info) -> bool;
consteval auto is_const(info) -> bool;
consteval auto is_volatile(info) -> bool;
consteval auto is_lvalue_reference_qualified(info) -> bool;
consteval auto is_rvalue_reference_qualified(info) -> bool;
consteval auto has_static_storage_duration(info) -> bool;
Expand Down Expand Up @@ -402,6 +404,8 @@ enum : unsigned {
__metafn_is_explicit,
__metafn_is_noexcept,
__metafn_is_bit_field,
__metafn_is_const,
__metafn_is_volatile,
__metafn_is_lvalue_reference_qualified,
__metafn_is_rvalue_reference_qualified,
__metafn_has_static_storage_duration,
Expand Down Expand Up @@ -934,6 +938,18 @@ consteval auto is_bit_field(info r) -> bool {
return __metafunction(detail::__metafn_is_bit_field, r);
}

// Returns whether the reflected type is const-qualified, or if the reflected
// entity is of such a type.
consteval auto is_const(info r) -> bool {
return __metafunction(detail::__metafn_is_const, r);
}

// Returns whether the reflected type is volatile-qualified, or if the reflected
// entity is of such a type.
consteval auto is_volatile(info r) -> bool {
return __metafunction(detail::__metafn_is_volatile, r);
}

// Returns whether the reflected member function or function type is
// lvalue-reference qualified.
consteval auto is_lvalue_reference_qualified(info r) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,4 +861,61 @@ static_assert(!has_default_member_initializer(^int));
static_assert(!has_default_member_initializer(^::));
} // namespace member_initializers

// ==================
// const_and_volatile
// ==================

namespace const_and_volatile {

struct S {
void fn1();
void fn2() const;
void fn3() volatile;
};

int v1;

const int v2 = 0;
const int arr1[] = {1, 2};

volatile int v3;
volatile int arr2[] = {1, 2};

static_assert(!is_const(^S::fn1));
static_assert(is_const(^S::fn2));
static_assert(!is_const(^S::fn3));

static_assert(!is_const(^v1));
static_assert(is_const(^v2));
static_assert(is_const(^arr1));
static_assert(is_const(std::meta::reflect_object(arr1[1])));
static_assert(!is_const(^v3));
static_assert(!is_const(^arr2));
static_assert(!is_const(std::meta::reflect_object(arr2[1])));

static_assert(!is_volatile(^S::fn1));
static_assert(!is_volatile(^S::fn2));
static_assert(is_volatile(^S::fn3));

static_assert(!is_volatile(^v1));
static_assert(!is_volatile(^v2));
static_assert(!is_volatile(^arr1));
static_assert(!is_volatile(std::meta::reflect_object(arr1[1])));
static_assert(is_volatile(^v3));
static_assert(is_volatile(^arr2));
static_assert(is_volatile(std::meta::reflect_object(arr2[1])));

static_assert(!is_const(^int));
static_assert(is_const(^const int));
static_assert(is_const(^const volatile int));
static_assert(!is_const(^volatile int));
static_assert(is_const(^void() const));

static_assert(!is_volatile(^int));
static_assert(!is_volatile(^const int));
static_assert(is_volatile(^const volatile int));
static_assert(is_volatile(^volatile int));
static_assert(is_volatile(^void() volatile));
} // namespace const_and_volatile

int main() { }

0 comments on commit c683e64

Please sign in to comment.