Fixes/improvements to 'substitute'.

This commit is contained in:
Dan Katz
2025-05-06 15:52:57 -04:00
parent a11f0d2d14
commit 16f4b13424
5 changed files with 58 additions and 87 deletions

View File

@@ -51,6 +51,9 @@ def metafn_parent_of_extern_c : Note<
// Substitution and invocation.
def metafn_cannot_be_arg : Note<
"a reflection of %0 cannot represent a %select{function|template}1 argument">;
def metafn_undeduced_placeholder : Note<
"cannot form a reflection of function %0 whose type %1 contains an undeduced "
"placeholder">;
def metafn_cannot_invoke : Note<"cannot invoke %0">;
def metafn_no_specialization_found : Note<
"no specialization of the function template %0 matched the provided "

View File

@@ -146,12 +146,6 @@ static bool template_of(APValue &Result, ASTContext &C, MetaActions &Meta,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);
static bool can_substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser,
bool AllowInjection, QualType ResultTy,
SourceRange Range, ArrayRef<Expr *> Args,
Decl *ContainingDecl);
static bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
@@ -744,8 +738,7 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_metaInfo, 1, 1, object_of },
{ Metafunction::MFRK_metaInfo, 1, 1, value_of },
{ Metafunction::MFRK_metaInfo, 1, 1, template_of },
{ Metafunction::MFRK_bool, 3, 3, can_substitute },
{ Metafunction::MFRK_metaInfo, 3, 3, substitute },
{ Metafunction::MFRK_metaInfo, 4, 4, substitute },
{ Metafunction::MFRK_spliceFromArg, 2, 2, extract },
{ Metafunction::MFRK_bool, 1, 1, is_public },
{ Metafunction::MFRK_bool, 1, 1, is_protected },
@@ -880,6 +873,10 @@ static APValue makeBool(ASTContext &C, bool B) {
return APValue(C.MakeIntValue(B, C.BoolTy));
}
static APValue makeReflection(std::nullptr_t) {
return APValue(ReflectionKind::Null, nullptr);
}
static APValue makeReflection(QualType QT) {
return APValue(ReflectionKind::Type, QT.getAsOpaquePtr());
}
@@ -2765,71 +2762,6 @@ static TemplateArgument TArgFromReflection(ASTContext &C, EvalFn Evaluator,
return TemplateArgument();
}
// TODO(P2996): Abstract this out, and use as an implementation detail of
// 'substitute'.
bool can_substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl) {
assert(Args[0]->getType()->isReflectionType());
assert(
Args[1]->getType()->getPointeeOrArrayElementType()->isReflectionType());
assert(Args[2]->getType()->isIntegerType());
APValue Template;
if (!Evaluator(Template, Args[0], true))
return true;
if (!Template.isReflectedTemplate())
return DiagnoseReflectionKind(Diagnoser, Range, "a template",
DescriptionOf(Template));
TemplateDecl *TDecl = Template.getReflectedTemplate().getAsTemplateDecl();
if (TDecl->isInvalidDecl())
return true;
SmallVector<TemplateArgument, 4> TArgs;
{
// Evaluate how many template arguments were provided.
APValue NumArgs;
if (!Evaluator(NumArgs, Args[2], true))
return true;
size_t nArgs = NumArgs.getInt().getExtValue();
TArgs.reserve(nArgs);
for (uint64_t k = 0; k < nArgs; ++k) {
llvm::APInt Idx(C.getTypeSize(C.getSizeType()), k, false);
Expr *Synthesized = IntegerLiteral::Create(C, Idx, C.getSizeType(),
Args[1]->getExprLoc());
Synthesized = new (C) ArraySubscriptExpr(Args[1], Synthesized,
C.MetaInfoTy, VK_LValue,
OK_Ordinary, Range.getBegin());
if (Synthesized->isValueDependent() || Synthesized->isTypeDependent())
return true;
APValue Unwrapped;
if (!Evaluator(Unwrapped, Synthesized, true) ||
!Unwrapped.isReflection())
return true;
Unwrapped = MaybeUnproxy(C, Unwrapped);
if (!CanActAsTemplateArg(Unwrapped))
return SetAndSucceed(Result, makeBool(C, false));
TemplateArgument TArg = TArgFromReflection(C, Evaluator, Unwrapped,
Range.getBegin());
if (TArg.isNull())
return true;
TArgs.push_back(TArg);
}
}
SmallVector<TemplateArgument, 4> ExpandedTArgs;
expandTemplateArgPacks(TArgs, ExpandedTArgs);
bool CanSub = Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, true,
Args[0]->getExprLoc());
return SetAndSucceed(Result, makeBool(C, CanSub));
}
bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range, ArrayRef<Expr *> Args,
@@ -2851,6 +2783,14 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
if (TDecl->isInvalidDecl())
return true;
APValue DiagnoseAPV;
if (!Evaluator(DiagnoseAPV, Args[3], true))
return true;
bool NoDiagnose = !DiagnoseAPV.getInt().getBoolValue();
auto ElideDiagnosis = [&] {
return SetAndSucceed(Result, makeReflection(nullptr));
};
SmallVector<TemplateArgument, 4> TArgs;
{
// Evaluate how many template arguments were provided.
@@ -2877,8 +2817,9 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
return true;
Unwrapped = MaybeUnproxy(C, Unwrapped);
if (!CanActAsTemplateArg(Unwrapped))
return Diagnoser(Range.getBegin(), diag::metafn_cannot_be_arg)
<< DescriptionOf(Unwrapped) << 1 << Range;
return NoDiagnose ? ElideDiagnosis() :
Diagnoser(Range.getBegin(), diag::metafn_cannot_be_arg)
<< DescriptionOf(Unwrapped) << 1 << Range;
TemplateArgument TArg = TArgFromReflection(C, Evaluator, Unwrapped,
Range.getBegin());
@@ -2902,9 +2843,9 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
if (C.checkCachedSubstitution(SubstitutionHash, &Result))
return false;
if (!Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, false,
if (!Meta.CheckTemplateArgumentList(TDecl, ExpandedTArgs, NoDiagnose,
Args[0]->getExprLoc()))
return true;
return NoDiagnose ? ElideDiagnosis() : true;
for (const auto &TArg : ExpandedTArgs)
if (TArg.getKind() == TemplateArgument::Expression &&
TArg.getAsExpr()->containsErrors())
@@ -2942,6 +2883,11 @@ bool substitute(APValue &Result, ASTContext &C, MetaActions &Meta,
FunctionDecl *Spec = Meta.Substitute(FTD, ExpandedTArgs, Range.getBegin());
assert(Spec && "substitution failed after validating arguments?");
if (Spec->getReturnType()->isUndeducedType())
return NoDiagnose ? ElideDiagnosis() :
Diagnoser(Range.getBegin(), diag::metafn_undeduced_placeholder)
<< Spec << Spec->getType() << Range;
APValue RV = makeReflection(Spec);
//C.recordCachedSubstitution(SubstitutionHash, RV);
return SetAndSucceed(Result, RV);

View File

@@ -26,7 +26,6 @@
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/Support/raw_ostream.h"
#include <iostream>
using namespace clang;
using namespace sema;

View File

@@ -446,7 +446,6 @@ enum : unsigned {
__metafn_object_of,
__metafn_value_of,
__metafn_template_of,
__metafn_can_substitute,
__metafn_substitute,
__metafn_extract,
__metafn_is_public,
@@ -907,14 +906,16 @@ consteval auto is_data_member_spec(info r) -> bool {
// Returns whether 'templ' substituted with 'args' forms a valid template-id.
template <reflection_range R = initializer_list<info>>
consteval auto can_substitute(info templ, R &&args) -> bool {
info sub;
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_can_substitute, templ,
ranges::data(args), ranges::size(args));
sub = __metafunction(detail::__metafn_substitute, templ,
ranges::data(args), ranges::size(args), false);
} else {
vector vargs = args | ranges::to<vector>();
return __metafunction(detail::__metafn_can_substitute, templ,
vargs.data(), vargs.size());
sub = __metafunction(detail::__metafn_substitute, templ,
vargs.data(), vargs.size(), false);
}
return sub != info{};
}
// Returns a reflection representing the template instantiation of the entity
@@ -923,11 +924,11 @@ template <reflection_range R = initializer_list<info>>
consteval auto substitute(info templ, R &&args) -> info {
if constexpr (ranges::contiguous_range<R>) {
return __metafunction(detail::__metafn_substitute, templ,
ranges::data(args), ranges::size(args));
ranges::data(args), ranges::size(args), true);
} else {
vector vargs = args | ranges::to<vector>();
return __metafunction(detail::__metafn_substitute, templ, vargs.data(),
vargs.size());
vargs.size(), true);
}
}

View File

@@ -10,7 +10,6 @@
// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20
// ADDITIONAL_COMPILE_FLAGS: -freflection
// ADDITIONAL_COMPILE_FLAGS: -Wno-unneeded-internal-declaration
// <experimental/reflection>
//
@@ -348,7 +347,7 @@ template <template <typename...> class... Cs>
using Alias = [:substitute(^^std::tuple, {substitute(^^Cs, {^^int})...}):];
static_assert(dealias(^^Alias<std::queue, std::vector>) ==
^^std::tuple<std::queue<int>, std::vector<int>>);
} // namespace equality_respects_default_template_arguments
} // namespace equality_respects_default_template_arguments
// ==========================
// with_template_arguments_of
@@ -462,4 +461,27 @@ template <auto &V> static constexpr auto &Value = V;
static_assert([:substitute(^^Value, {members_of(^^Cls, ctx)[0]}):] == 11);
} // namespace non_type_ref_regression_test
// ===============
// wording_example
// ===============
namespace wording_example {
template <typename T>
auto fn1();
static_assert(!can_substitute(^^fn1, {^^int}));
constexpr auto r1 = substitute(^^fn1, {^^int});
// expected-error@-1 {{must be initialized by a constant expression}} \
// expected-note@-1 {{undeduced placeholder}}
template <typename T>
auto fn2() {
static_assert(^^T != ^^int); // expected-error {{static assertion failed}}
return 0;
}
constexpr auto r2 = substitute(^^fn2, {^^int});
// expected-note@-1 {{requested here}}
} // namespace wording_example
int main() { }