Skip to content

Commit

Permalink
Implement P3096R1.
Browse files Browse the repository at this point in the history
Closes issue llvm#52.
  • Loading branch information
katzdm committed Jun 13, 2024
1 parent 3473c41 commit f4b2be3
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 37 deletions.
182 changes: 162 additions & 20 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,27 @@ static bool has_consistent_name(APValue &Result, Sema &S, EvalFn Evaluator,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args);

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

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

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

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

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

// -----------------------------------------------------------------------------
// Metafunction table
//
Expand All @@ -342,7 +359,7 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_metaInfo, 1, 1, map_decl_to_entity },

// exposed metafunctions
{ Metafunction::MFRK_spliceFromArg, 3, 3, name_of },
{ Metafunction::MFRK_spliceFromArg, 4, 4, name_of },
{ Metafunction::MFRK_spliceFromArg, 3, 3, display_name_of },
{ Metafunction::MFRK_sourceLoc, 1, 1, source_location_of },
{ Metafunction::MFRK_metaInfo, 1, 1, type_of },
Expand Down Expand Up @@ -404,7 +421,11 @@ static constexpr Metafunction Metafunctions[] = {
// P3096 metafunction extensions
{ Metafunction::MFRK_metaInfo, 3, 3, get_ith_parameter_of },
{ Metafunction::MFRK_bool, 1, 1, has_consistent_name },
{ Metafunction::MFRK_bool, 1, 1, has_ellipsis_parameter },
{ Metafunction::MFRK_bool, 1, 1, has_default_argument },
{ Metafunction::MFRK_bool, 1, 1, is_explicit_object_parameter },
{ Metafunction::MFRK_bool, 1, 1, is_function_parameter },
{ Metafunction::MFRK_metaInfo, 1, 1, return_type_of },
};
constexpr const unsigned NumMetafunctions = sizeof(Metafunctions) /
sizeof(Metafunction);
Expand Down Expand Up @@ -532,6 +553,25 @@ static void getTypeName(std::string &Result, ASTContext &C, QualType QT,
encodeName(Result, QT.getAsString(PP), BasicOnly);
}

static bool parameterHasConsistentName(ParmVarDecl *PVD) {
StringRef FirstNameSeen = PVD->getName();
unsigned ParamIdx = PVD->getFunctionScopeIndex();

while (PVD) {
FunctionDecl *FD = cast<FunctionDecl>(PVD->getDeclContext());
FD = FD->getPreviousDecl();
if (!FD)
return true;

PVD = FD->getParamDecl(ParamIdx);
assert(PVD);
if (StringRef Name = PVD->getName();
Name.size() > 0 && Name != FirstNameSeen)
return false;
}
return true;
}

static void getDeclName(std::string &Result, ASTContext &C, Decl *D,
bool BasicOnly) {
PrintingPolicy PP = C.getPrintingPolicy();
Expand Down Expand Up @@ -1341,13 +1381,25 @@ bool name_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
IsUtf8 = Scratch.getInt().getBoolValue();
}

bool EnforceConsistent;
{
APValue Scratch;
if (!Evaluator(Scratch, Args[3], true))
return true;
EnforceConsistent = Scratch.getInt().getBoolValue();
}

std::string Name;
switch (R.getReflection().getKind()) {
case ReflectionValue::RK_type: {
getTypeName(Name, S.Context, R.getReflectedType(), !IsUtf8);
return !Evaluator(Result, makeCString(Name, S.Context, IsUtf8), true);
}
case ReflectionValue::RK_declaration: {
if (auto *PVD = dyn_cast<ParmVarDecl>(R.getReflectedDecl());
EnforceConsistent && PVD && !parameterHasConsistentName(PVD))
return true;

getDeclName(Name, S.Context, R.getReflectedDecl(), !IsUtf8);
return !Evaluator(Result, makeCString(Name, S.Context, IsUtf8), true);
}
Expand Down Expand Up @@ -1486,8 +1538,11 @@ bool type_of(APValue &Result, Sema &S, EvalFn Evaluator, QualType ResultTy,
}
case ReflectionValue::RK_declaration: {
ValueDecl *VD = cast<ValueDecl>(R.getReflectedDecl());
QualType QT = desugarType(VD->getType(), /*UnwrapAliases=*/false,
/*DropCV=*/false, /*DropRefs=*/false);

bool UnwrapAliases = isa<ParmVarDecl>(VD);
bool DropCV = isa<ParmVarDecl>(VD);
QualType QT = desugarType(VD->getType(), UnwrapAliases, DropCV,
/*DropRefs=*/false);
return SetAndSucceed(Result, makeReflection(QT));
}
case ReflectionValue::RK_base_specifier: {
Expand Down Expand Up @@ -3947,24 +4002,43 @@ bool has_consistent_name(APValue &Result, Sema &S, EvalFn Evaluator,
return true;
case ReflectionValue::RK_declaration: {
if (auto *PVD = dyn_cast<ParmVarDecl>(R.getReflectedDecl())) {
StringRef FirstNameSeen = PVD->getName();
ParmVarDecl *FirstParmSeen = PVD;

bool Unique = true;
while (PVD) {
FunctionDecl *FD = cast<FunctionDecl>(PVD->getDeclContext());
FD = FD->getPreviousDecl();
if (!FD)
break;
bool Consistent = parameterHasConsistentName(PVD);
return SetAndSucceed(Result, makeBool(S.Context, Consistent));
}
return true;
}
}
llvm_unreachable("unknown reflection kind");
}

PVD = FD->getParamDecl(FirstParmSeen->getFunctionScopeIndex());
assert(PVD);
if (PVD->getName() != FirstNameSeen) {
Unique = false;
break;
}
}
return SetAndSucceed(Result, makeBool(S.Context, Unique));
bool has_ellipsis_parameter(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_expr_result:
case ReflectionValue::RK_template:
case ReflectionValue::RK_namespace:
case ReflectionValue::RK_base_specifier:
case ReflectionValue::RK_data_member_spec:
return true;
case ReflectionValue::RK_type:
if (auto *FPT = dyn_cast<FunctionProtoType>(R.getReflectedType())) {
bool HasEllipsis = FPT->isVariadic();
return SetAndSucceed(Result, makeBool(S.Context, HasEllipsis));
}
return true;
case ReflectionValue::RK_declaration: {
if (auto *FD = dyn_cast<FunctionDecl>(R.getReflectedDecl())) {
bool HasEllipsis = FD->getEllipsisLoc().isValid();
return SetAndSucceed(Result, makeBool(S.Context, HasEllipsis));
}
return true;
}
Expand Down Expand Up @@ -4001,5 +4075,73 @@ bool has_default_argument(APValue &Result, Sema &S, EvalFn Evaluator,
llvm_unreachable("unknown reflection kind");
}

bool is_explicit_object_parameter(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) {
if (auto *PVD = dyn_cast<ParmVarDecl>(R.getReflectedDecl()))
result = PVD->isExplicitObjectParameter();
}
return SetAndSucceed(Result, makeBool(S.Context, result));
}

bool is_function_parameter(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 ParmVarDecl>(R.getReflectedDecl());
}
return SetAndSucceed(Result, makeBool(S.Context, result));
}

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

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

switch (R.getReflection().getKind()) {
case ReflectionValue::RK_null:
case ReflectionValue::RK_expr_result:
case ReflectionValue::RK_template:
case ReflectionValue::RK_namespace:
case ReflectionValue::RK_base_specifier:
case ReflectionValue::RK_data_member_spec:
return true;
case ReflectionValue::RK_type:
if (auto *FPT = dyn_cast<FunctionProtoType>(R.getReflectedType()))
return SetAndSucceed(Result, makeReflection(FPT->getReturnType()));

return true;
case ReflectionValue::RK_declaration: {
if (auto *FD = dyn_cast<FunctionDecl>(R.getReflectedDecl());
FD && !isa<CXXConstructorDecl>(FD) && !isa<CXXDestructorDecl>(FD))
return SetAndSucceed(Result, makeReflection(FD->getReturnType()));

return true;
}
}
llvm_unreachable("unknown reflection kind");
}

} // end namespace clang
40 changes: 39 additions & 1 deletion libcxx/include/experimental/meta
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,13 @@ consteval auto alignment_of(info) -> size_t;
// function parameters (P3096)
consteval auto parameters_of(info) -> vector<info>;
consteval auto has_consistent_name(info) -> bool;
template<typename T = u8string_view>
consteval auto any_name_of(info) -> string_view;
consteval auto has_ellipsis_parameter(info) -> bool;
consteval auto has_default_argument(info) -> bool;
consteval auto is_explicit_object_parameter(info) -> bool;
consteval auto is_function_parameter(info) -> bool;
consteval auto return_type_of(info) -> info;
} // namespace reflection_v2
} // namespace meta
Expand Down Expand Up @@ -249,7 +255,11 @@ enum : unsigned {
// P3096 metafunctions
__metafn_get_ith_parameter_of,
__metafn_has_consistent_name,
__metafn_has_ellipsis_parameter,
__metafn_has_default_argument,
__metafn_is_explicit_object_parameter,
__metafn_is_function_parameter,
__metafn_return_type_of,
};

} // namespace detail
Expand Down Expand Up @@ -484,7 +494,8 @@ consteval auto name_of(info r) -> T {

return __metafunction(detail::__metafn_name_of,
^const typename T::value_type *, r,
^T == dealias(^std::u8string_view));
^T == dealias(^std::u8string_view),
/*EnforceConsistent=*/true);
}

// Returns a name for the reflected entity.
Expand Down Expand Up @@ -1040,10 +1051,37 @@ consteval auto has_consistent_name(info r) -> bool {
return __metafunction(detail::__metafn_has_consistent_name, r);
}

template<typename T = u8string_view>
consteval auto any_name_of(info r) -> T {
static_assert(^T == dealias(^u8string_view) || ^T == dealias(^string_view),
"Name type must be 'string_view' or 'u8string_view'");

return __metafunction(detail::__metafn_name_of,
^const typename T::value_type *, r,
^T == dealias(^std::u8string_view),
/*EnforceConsistent=*/false);
}

consteval auto has_ellipsis_parameter(info r) -> bool {
return __metafunction(detail::__metafn_has_ellipsis_parameter, r);
}

consteval auto has_default_argument(info r) -> bool {
return __metafunction(detail::__metafn_has_default_argument, r);
}

consteval auto is_explicit_object_parameter(info r) -> bool {
return __metafunction(detail::__metafn_is_explicit_object_parameter, r);
}

consteval auto is_function_parameter(info r) -> bool {
return __metafunction(detail::__metafn_is_function_parameter, r);
}

consteval auto return_type_of(info r) -> info {
return __metafunction(detail::__metafn_return_type_of, r);
}

#endif // __has_feature(parameter_reflection)

template <typename Ty>
Expand Down
Loading

0 comments on commit f4b2be3

Please sign in to comment.