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

Issue #45: Implement is_noexcept meta function #56

Merged
merged 15 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
68 changes: 68 additions & 0 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ static bool is_explicit(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);

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

static bool is_bit_field(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);
Expand Down Expand Up @@ -358,6 +362,7 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_bool, 1, 1, is_deleted },
{ Metafunction::MFRK_bool, 1, 1, is_defaulted },
{ 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, has_static_storage_duration },
{ Metafunction::MFRK_bool, 1, 1, has_internal_linkage },
Expand Down Expand Up @@ -1006,6 +1011,37 @@ bool isAccessible(Sema &S, DeclContext *AccessDC, NamedDecl *D) {
return Result;
}

template <typename T>
static bool isFunctionDeclNoexcept(const T *FuncD) {
switch (FuncD->getExceptionSpecType()) {
case EST_BasicNoexcept:
case EST_NoexceptTrue:
return true;
default:
return false;
}
}

static bool isFunctionOrLambdaNoexcept(const QualType QT) {
const Type* T = QT.getTypePtr();

if (T->isFunctionProtoType()) {
// This covers (virtual) methods & functions
const auto *FPT = T->getAs<FunctionProtoType>();

return isFunctionDeclNoexcept(FPT);
} else if (T->isRecordType()) {
// This branch is for lambdas only
const auto RT = T->getAs<RecordType>();
const auto RecordD = cast<CXXRecordDecl>(RT->getDecl());

if (RecordD && RecordD->isLambda() && !RecordD->isGenericLambda())
return isFunctionDeclNoexcept(RecordD->getLambdaCallOperator());
}

return false;
}

// -----------------------------------------------------------------------------
// Metafunction implementations
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -2508,6 +2544,38 @@ bool is_explicit(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
llvm_unreachable("invalid reflection type");
}

bool is_noexcept(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_const_value:
case ReflectionValue::RK_namespace:
case ReflectionValue::RK_base_specifier:
case ReflectionValue::RK_data_member_spec:
case ReflectionValue::RK_template:
return SetAndSucceed(Result, makeBool(S.Context, false));
case ReflectionValue::RK_type: {
const QualType QT = R.getReflectedType();
const auto result = isFunctionOrLambdaNoexcept(QT);

return SetAndSucceed(Result, makeBool(S.Context, result));
}
case ReflectionValue::RK_declaration: {
const ValueDecl *D = R.getReflectedDecl();
const auto result = isFunctionOrLambdaNoexcept(D->getType());

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

bool is_bit_field(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 @@ -76,6 +76,7 @@ consteval auto is_override(info) -> bool;
consteval auto is_deleted(info) -> bool;
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 has_static_storage_duration(info) -> bool;
consteval auto has_internal_linkage(info) -> bool;
Expand Down Expand Up @@ -183,6 +184,7 @@ enum : unsigned {
__metafn_is_deleted,
__metafn_is_defaulted,
__metafn_is_explicit,
__metafn_is_noexcept,
__metafn_is_bit_field,
__metafn_has_static_storage_duration,
__metafn_has_internal_linkage,
Expand Down Expand Up @@ -692,6 +694,11 @@ consteval auto is_explicit(info r) -> bool {
return __metafunction(detail::__metafn_is_explicit, r);
}

// Returns whether the reflected member function, it's type, closure type of a non-generic lambda or it's value is noexcept.
consteval auto is_noexcept(info r) -> bool {
return __metafunction(detail::__metafn_is_noexcept, r);
}

// Returns whether the reflected class data member is a bit field.
consteval auto is_bit_field(info r) -> bool {
return __metafunction(detail::__metafn_is_bit_field, r);
Expand Down
148 changes: 148 additions & 0 deletions libcxx/test/std/experimental/reflection/member-classification.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,154 @@ static_assert(!is_explicit(^::));
static_assert(!is_explicit(^inner));
} // namespace explicit_functions

// ==================
// noexcept_functions
// ==================

namespace noexcept_functions {
struct S {
// methods
void noexcept_method() noexcept;
void noexcept_true_method() noexcept(true);
void noexcept_false_method() noexcept(false);
void not_noexcept_method();

// virtual methods
// w/o defining it complains about vtable
virtual void noexcept_virtual_method() noexcept {}
virtual void noexcept_true_virtual_method() noexcept(true) {}
virtual void noexcept_false_virtual_method() noexcept(false) {}
virtual void not_noexcept_virtual_method() {}

// template methods
template <typename T>
void noexcept_template_method() noexcept;
template <typename T>
void noexcept_true_template_method() noexcept(true);
template <typename T>
void noexcept_false_template_method() noexcept(false);
template <typename T>
void not_noexcept_template_method();
};

// non generic lambdas
constexpr auto noexcept_lambda = []() noexcept {};
constexpr auto not_noexcept_lambda = []{};

// generic lambdas
constexpr auto noexcept_generic_lambda = []<typename T>() noexcept {};
constexpr auto not_noexcept_generic_lambda = []<typename T>() {};

// functions
void noexcept_function() noexcept;
void noexcept_true_function() noexcept(true);
void noexcept_false_function() noexcept(false);
void not_noexcept_function();

// template functions
template <typename T>
void noexcept_template_function() noexcept;
template <typename T>
void noexcept_true_template_function() noexcept(true);
template <typename T>
void noexcept_false_template_function() noexcept(false);
template <typename T>
void not_noexcept_template_function();

// Everything mentioned in the proposal
// (no-)noexcept member functions
static_assert(is_noexcept(^S::noexcept_method));
static_assert(is_noexcept(^S::noexcept_true_method));
static_assert(!is_noexcept(^S::noexcept_false_method));
static_assert(!is_noexcept(^S::not_noexcept_method));

// (no-)noexcept member function types
static_assert(is_noexcept(type_of(^S::noexcept_method)));
static_assert(is_noexcept(type_of(^S::noexcept_true_method)));
static_assert(!is_noexcept(type_of(^S::noexcept_false_method)));
static_assert(!is_noexcept(type_of(^S::not_noexcept_method)));

// (no-)noexcept virtual methods
static_assert(is_noexcept(^S::noexcept_virtual_method));
static_assert(is_noexcept(^S::noexcept_true_virtual_method));
static_assert(!is_noexcept(^S::noexcept_false_virtual_method));
static_assert(!is_noexcept(^S::not_noexcept_virtual_method));

// (no-)noexcept virtual method types
static_assert(is_noexcept(type_of(^S::noexcept_virtual_method)));
static_assert(is_noexcept(type_of(^S::noexcept_true_virtual_method)));
static_assert(!is_noexcept(type_of(^S::noexcept_false_virtual_method)));
static_assert(!is_noexcept(type_of(^S::not_noexcept_virtual_method)));

// (no-)noexcept instantiated template methods
static_assert(is_noexcept(^S::noexcept_template_method<int>));
static_assert(is_noexcept(^S::noexcept_true_template_method<int>));
static_assert(!is_noexcept(^S::noexcept_false_template_method<int>));
static_assert(!is_noexcept(^S::not_noexcept_template_method<int>));

// (no-)noexcept instantiated template method types
static_assert(is_noexcept(type_of(^S::noexcept_template_method<int>)));
static_assert(is_noexcept(type_of(^S::noexcept_true_template_method<int>)));
static_assert(!is_noexcept(type_of(^S::noexcept_false_template_method<int>)));
static_assert(!is_noexcept(type_of(^S::not_noexcept_template_method<int>)));

// (no-)noexcept lambdas
static_assert(is_noexcept(^noexcept_lambda));
static_assert(!is_noexcept(^not_noexcept_lambda));

// (no-)noexcept closure types
static_assert(is_noexcept(type_of(^noexcept_lambda)));
static_assert(!is_noexcept(type_of(^not_noexcept_lambda)));

// (no-)noexcept function
static_assert(is_noexcept(^noexcept_function));
static_assert(is_noexcept(^noexcept_true_function));
static_assert(!is_noexcept(^noexcept_false_function));
static_assert(!is_noexcept(^not_noexcept_function));

// (no-)noexcept function type
static_assert(is_noexcept(type_of(^noexcept_function)));
static_assert(is_noexcept(type_of(^noexcept_true_function)));
static_assert(!is_noexcept(type_of(^noexcept_false_function)));
static_assert(!is_noexcept(type_of(^not_noexcept_function)));

// (no-)noexcept instantiated template functions
static_assert(is_noexcept(^noexcept_template_function<int>));
static_assert(is_noexcept(^noexcept_true_template_function<int>));
static_assert(!is_noexcept(^noexcept_false_template_function<int>));
static_assert(!is_noexcept(^not_noexcept_template_function<int>));

// (no-)noexcept instantiated template function types
static_assert(is_noexcept(type_of(^noexcept_template_function<int>)));
static_assert(is_noexcept(type_of(^noexcept_true_template_function<int>)));
static_assert(!is_noexcept(type_of(^noexcept_false_template_function<int>)));
static_assert(!is_noexcept(type_of(^not_noexcept_template_function<int>)));

// The rest (should all be false regardless of noexcept specifier)
// (no-)noexcept non-instantiated template methods
static_assert(!is_noexcept(^S::noexcept_template_method));
static_assert(!is_noexcept(^S::not_noexcept_template_method));

// (no-)noexcept generic closure types
static_assert(!is_noexcept(^noexcept_generic_lambda));
static_assert(!is_noexcept(^not_noexcept_generic_lambda));
static_assert(!is_noexcept(type_of(^noexcept_generic_lambda)));
static_assert(!is_noexcept(type_of(^not_noexcept_generic_lambda)));

// (no-)noexcept non-instantiated template functions
static_assert(!is_noexcept(^noexcept_template_function));
static_assert(!is_noexcept(^not_noexcept_template_function));

// Random non callable types
auto x = 1;
auto s = S();

static_assert(!is_noexcept(^x));
static_assert(!is_noexcept(type_of(^x)));
static_assert(!is_noexcept(^s));
static_assert(!is_noexcept(type_of(^s)));
} // namespace noexcept_functions

// ================
// bitfield members
// ================
Expand Down