Merge branch 'p2996' into wip

This commit is contained in:
Dan Katz
2025-05-07 12:59:32 -04:00
17 changed files with 351 additions and 154 deletions

View File

@@ -1026,6 +1026,7 @@ public:
let HasCustomParsing = 1;
let TemplateDependent = 1;
let MeaningfulToClassTemplateDefinition = 1;
let Documentation = [InternalOnly];
}

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

@@ -2023,7 +2023,8 @@ CXXSpliceExpr::CXXSpliceExpr(QualType ResultTy, ExprValueKind ValueKind,
SourceLocation TemplateKWLoc,
SpliceSpecifier *Splice, Expr *Model,
bool AllowMemberReference)
: Expr(CXXSpliceExprClass, ResultTy, ValueKind, OK_Ordinary),
: Expr(CXXSpliceExprClass, ResultTy, ValueKind,
Model ? Model->getObjectKind() : OK_Ordinary),
TemplateKWLoc(TemplateKWLoc), Splice(Splice), Model(Model),
AllowMemberReference(AllowMemberReference) {
setDependence(computeDependence(this));
@@ -2078,7 +2079,7 @@ ExplDependentCallExpr::ExplDependentCallExpr(CallExpr *SubExpr,
unsigned TemplateDepth)
: Expr(ExplDependentCallExprClass, SubExpr->getType(),
SubExpr->getValueKind(), OK_Ordinary),
SubExpr(SubExpr), TemplateDepth(TemplateDepth) {
TemplateDepth(TemplateDepth), SubExpr(SubExpr) {
setDependence(computeDependence(this));
}

View File

@@ -126,10 +126,17 @@ static bool parent_of(APValue &Result, ASTContext &C, MetaActions &Meta,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);
static bool dealias(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);
static bool underlying_entity_of(APValue &Result, ASTContext &C,
MetaActions &Meta, EvalFn Evaluator,
DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);
static bool proxied_entity_of(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser,
bool AllowInjection, QualType ResultTy,
SourceRange Range, ArrayRef<Expr *> Args,
Decl *ContainingDecl);
static bool value_of(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
@@ -146,12 +153,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,
@@ -740,12 +741,12 @@ static constexpr Metafunction Metafunctions[] = {
{ Metafunction::MFRK_sourceLoc, 1, 1, source_location_of },
{ Metafunction::MFRK_metaInfo, 1, 1, type_of },
{ Metafunction::MFRK_metaInfo, 1, 1, parent_of },
{ Metafunction::MFRK_metaInfo, 1, 1, dealias },
{ Metafunction::MFRK_metaInfo, 1, 1, underlying_entity_of },
{ Metafunction::MFRK_metaInfo, 1, 1, proxied_entity_of },
{ 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 +881,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());
}
@@ -1767,7 +1772,11 @@ bool get_ith_base_of(APValue &Result, ASTContext &C, MetaActions &Meta,
switch (RV.getReflectionKind()) {
case ReflectionKind::Type: {
Decl *typeDecl = findTypeDecl(RV.getReflectedType());
QualType QT = RV.getReflectedType();
QT = desugarType(QT, /*UnwrapAliases=*/true, /*DropCV=*/false,
/*DropRefs=*/false);
Decl *typeDecl = findTypeDecl(QT);
if (auto cxxRecordDecl = dyn_cast_or_null<CXXRecordDecl>(typeDecl)) {
Meta.EnsureInstantiated(typeDecl, Range);
@@ -2456,10 +2465,11 @@ bool parent_of(APValue &Result, ASTContext &C, MetaActions &Meta,
llvm_unreachable("unknown reflection kind");
}
bool dealias(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range, ArrayRef<Expr *> Args,
Decl *ContainingDecl) {
bool underlying_entity_of(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(ResultTy == C.MetaInfoTy);
@@ -2495,6 +2505,35 @@ bool dealias(APValue &Result, ASTContext &C, MetaActions &Meta,
llvm_unreachable("unknown reflection kind");
}
bool proxied_entity_of(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(ResultTy == C.MetaInfoTy);
APValue RV;
if (!Evaluator(RV, Args[0], true))
return true;
switch (RV.getReflectionKind()) {
case ReflectionKind::Null:
case ReflectionKind::Type:
case ReflectionKind::Object:
case ReflectionKind::Value:
case ReflectionKind::Declaration:
case ReflectionKind::Namespace:
case ReflectionKind::Template:
case ReflectionKind::BaseSpecifier:
case ReflectionKind::DataMemberSpec:
case ReflectionKind::Annotation:
return DiagnoseReflectionKind(Diagnoser, Range, "an entity proxy");
case ReflectionKind::EntityProxy:
return SetAndSucceed(Result, MaybeUnproxy(C, RV, false));
}
llvm_unreachable("unknown reflection kind");
}
bool object_of(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range, ArrayRef<Expr *> Args,
@@ -2761,71 +2800,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,
@@ -2847,6 +2821,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.
@@ -2873,8 +2855,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());
@@ -2898,9 +2881,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())
@@ -2938,6 +2921,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);
@@ -4797,23 +4785,41 @@ bool reflect_result(APValue &Result, ASTContext &C, MetaActions &Meta,
if (!Evaluator(Arg, Args[1], !IsLValue))
return true;
// Construct an expression whose result is 'Arg', and evaluate it to check if
// it's an allowed result of a constant template argument.
//
// This is just a hack to get 'CheckConstantExpression' in ExprConstant.cpp
// called on 'Arg', to diagnose cases like string literals and temporaries
// that aren't allowed in template arguments.
//
// The expression is constructed in three layers:
// - A ConstantExpr to hold 'Arg'
// - An OpaqueValueExpr to act as the ConstantExpr's subexpression (we can
// otherwise ICE when e.g., checking source location of the ConstantExpr)
// - An OpaqueValueExpr wrapper around the ConstantExpr to prevent
// EvaluateAsConstantExpr from grabbing 'Arg' and short-circuiting the
// evaluation (and, more imporantly, the result validation).
Expr *OVE = new (C) OpaqueValueExpr(Range.getBegin(), Args[1]->getType(),
IsLValue ? VK_LValue : VK_PRValue);
Expr *CE = ConstantExpr::Create(C, OVE, Arg);
{
Expr *CE = ConstantExpr::Create(C, OVE, Arg);
OVE = new (C) OpaqueValueExpr(Range.getBegin(), Args[1]->getType(),
CE->getValueKind(), OK_Ordinary, CE);
}
{
Expr::EvalResult Discarded;
ConstantExprKind CEKind = (CE->getType()->isClassType() && !IsLValue) ?
ConstantExprKind CEKind = (OVE->getType()->isClassType() && !IsLValue) ?
ConstantExprKind::ClassTemplateArgument :
ConstantExprKind::NonClassTemplateArgument;
if (!CE->EvaluateAsConstantExpr(Discarded, C, CEKind))
if (!OVE->EvaluateAsConstantExpr(Discarded, C, CEKind))
return Diagnoser(Range.getBegin(), diag::metafn_result_not_representable)
<< (IsLValue ? 1 : 0) << Range;
}
// If this is an lvalue to a function, promote the result to reflect
// the declaration.
if (CE->getType()->isFunctionType() && Arg.isLValue() &&
if (OVE->getType()->isFunctionType() && Arg.isLValue() &&
Arg.getLValueOffset().isZero())
if (!Arg.hasLValuePath() || Arg.getLValuePath().size() == 0)
if (APValue::LValueBase LVBase = Arg.getLValueBase();

View File

@@ -8741,6 +8741,10 @@ class BuiltinCandidateTypeSet {
/// candidate set.
bool HasNullPtrType;
/// A flag indicating whether the reflection type was present in the
/// candidate set.
bool HasReflectionType;
/// Sema - The semantic analysis instance where we are building the
/// candidate type set.
Sema &SemaRef;
@@ -8760,6 +8764,7 @@ public:
: HasNonRecordTypes(false),
HasArithmeticOrEnumeralTypes(false),
HasNullPtrType(false),
HasReflectionType(false),
SemaRef(SemaRef),
Context(SemaRef.Context) { }
@@ -8784,6 +8789,7 @@ public:
bool hasNonRecordTypes() { return HasNonRecordTypes; }
bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
bool hasNullPtrType() const { return HasNullPtrType; }
bool hasReflectionType() const { return HasReflectionType; }
};
} // end anonymous namespace
@@ -8965,6 +8971,8 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
MatrixTypes.insert(Ty);
} else if (Ty->isNullPtrType()) {
HasNullPtrType = true;
} else if (Ty->isReflectionType()) {
HasReflectionType = true;
} else if (AllowUserConversions && TyRec) {
// No conversion functions in incomplete types.
if (!SemaRef.isCompleteType(Loc, Ty))
@@ -9445,6 +9453,14 @@ public:
S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
}
}
if (CandidateTypes[ArgIdx].hasReflectionType()) {
CanQualType InfoTy = S.Context.getCanonicalType(S.Context.MetaInfoTy);
if (AddedTypes.insert(InfoTy).second) {
QualType ParamTypes[2] = { InfoTy, InfoTy };
S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
}
}
}
}

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

@@ -737,10 +737,16 @@ void Sema::InstantiateAttrsForDecl(
// FIXME: This function is called multiple times for the same template
// specialization. We should only instantiate attributes that were added
// since the previous instantiation.
bool AddAnnotations = New->attrs().empty();
for (const auto *TmplAttr : Tmpl->attrs()) {
if (!isRelevantAttr(*this, New, TmplAttr))
continue;
if (isa<CXX26AnnotationAttr>(TmplAttr) && !AddAnnotations)
// See https://github.com/llvm/llvm-project/issues/138596
// Just skip here to avoid duplication of the attribute.
continue;
// FIXME: If any of the special case versions from InstantiateAttrs become
// applicable to template declaration, we'll need to add them here.
CXXThisScopeRAII ThisScope(
@@ -778,6 +784,7 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
const Decl *Tmpl, Decl *New,
LateInstantiatedAttrVec *LateAttrs,
LocalInstantiationScope *OuterMostScope) {
bool AddAnnotations = New->attrs().empty();
for (const auto *TmplAttr : Tmpl->attrs()) {
if (!isRelevantAttr(*this, New, TmplAttr))
continue;
@@ -879,6 +886,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}
if (isa<CXX26AnnotationAttr>(TmplAttr) && !AddAnnotations)
// See https://github.com/llvm/llvm-project/issues/138596
// Just skip here to avoid duplication of the attribute.
continue;
// Existing DLL attribute on the instantiation takes precedence.
if (TmplAttr->getKind() == attr::DLLExport ||
TmplAttr->getKind() == attr::DLLImport) {

View File

@@ -91,6 +91,7 @@
// CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance)
// CHECK-NEXT: IFunc (SubjectMatchRule_function)
// CHECK-NEXT: InitPriority (SubjectMatchRule_variable)
// CHECK-NEXT: InstantiationDependent (SubjectMatchRule_function)
// CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
// CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record)
// CHECK-NEXT: Leaf (SubjectMatchRule_function)

View File

@@ -156,7 +156,10 @@ consteval info local_var_reflection() {
}
static_assert(local_var_reflection() == local_var_reflection());
// Compare reflections of the same local variable in different stack frames.
// ======================================
// local_variables_different_stack_frames
// ======================================
namespace local_variables_different_stack_frames {
consteval bool local_var_in_diff_frames_equal(info inf, int call_depth = 0) {
int lcl = call_depth;
@@ -172,3 +175,16 @@ consteval bool local_var_in_diff_frames_equal(info inf, int call_depth = 0) {
}
} // namespace local_variables_different_stack_frames
// ==============================
// defaulted_comparison_operators
// ==============================
namespace defaulted_comparison_operators {
struct S {
info mem;
consteval bool operator==(const S &) const = default;
};
static_assert(S{} == S{});
} // namespace defaulted_comparison_operators

View File

@@ -8,7 +8,7 @@
//
//===----------------------------------------------------------------------===//
//
// RUN: %clang_cc1 %s -std=c++26 -freflection -fentity-proxy-reflection
// RUN: %clang_cc1 %s -std=c++26 -freflection -fentity-proxy-reflection -verify
using info = decltype(^^int);
@@ -283,6 +283,21 @@ static_assert(int([:rB:]) == int([:rClsB:]));
static_assert(static_cast<Enum>([:rClsB:]) == B);
} // namespace with_enums
// ====================
// address_of_bit_field
// ====================
namespace address_of_bit_field {
struct S {
int x : 4, y : 4;
};
constexpr auto f() {
constexpr auto r = ^^S::y;
return &[:r:]; // expected-error {{address of bit-field requested}}
}
} // namespace address_of_bit_field
// =============
// colon_parsing
// =============

View File

@@ -442,11 +442,11 @@ enum : unsigned {
__metafn_source_location_of,
__metafn_type_of,
__metafn_parent_of,
__metafn_dealias,
__metafn_underlying_entity_of,
__metafn_proxied_entity_of,
__metafn_object_of,
__metafn_value_of,
__metafn_template_of,
__metafn_can_substitute,
__metafn_substitute,
__metafn_extract,
__metafn_is_public,
@@ -551,6 +551,10 @@ enum : unsigned {
consteval auto __workaround_expand_compiler_builtins(info type) -> info;
consteval auto __underlying_entity_of(info r) -> info {
return __metafunction(detail::__metafn_underlying_entity_of, r);
}
} // namespace detail
namespace __range_of_infos {
@@ -764,10 +768,23 @@ struct front_annotation_of {
// -----------------------------------------------------------------------------
// Returns a reflection of the canonical type for the reflected type.
#if __has_feature(entity_proxy_reflection)
[[deprecated("renamed to 'underlying_entity_of' in PXYZ")]]
#endif
consteval auto dealias(info r) -> info {
return __metafunction(detail::__metafn_dealias, r);
return __metafunction(detail::__metafn_underlying_entity_of, r);
}
#if __has_feature(entity_proxy_reflection)
consteval auto underlying_entity_of(info r) -> info {
return __metafunction(detail::__metafn_underlying_entity_of, r);
}
consteval auto proxied_entity_of(info r) -> info {
return __metafunction(detail::__metafn_proxied_entity_of, r);
}
#endif
// Returns the identifier for the represented entity. If the entity is a
// literal operator or literal operator template, then this is the identifier
// suffix for the literal operator.
@@ -907,14 +924,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 +942,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);
}
}
@@ -1906,100 +1925,103 @@ consteval auto is_nothrow_invocable_r_type(info result, info type,
consteval auto remove_const(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_const_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_const_t, {type})));
}
consteval auto remove_volatile(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_volatile_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_volatile_t, {type})));
}
consteval auto remove_cv(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_cv_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_cv_t, {type})));
}
consteval auto add_const(info type) -> info {
return dealias(substitute(^^add_const_t, {type}));
return detail::__underlying_entity_of(substitute(^^add_const_t, {type}));
}
consteval auto add_volatile(info type) -> info {
return dealias(substitute(^^add_volatile_t, {type}));
return detail::__underlying_entity_of(substitute(^^add_volatile_t, {type}));
}
consteval auto add_cv(info type) -> info {
return dealias(substitute(^^add_cv_t, {type}));
return detail::__underlying_entity_of(substitute(^^add_cv_t, {type}));
}
consteval auto remove_reference(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_reference_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_reference_t, {type})));
}
consteval auto add_lvalue_reference(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^add_lvalue_reference_t, {type})));
detail::__underlying_entity_of(substitute(^^add_lvalue_reference_t,
{type})));
}
consteval auto add_rvalue_reference(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^add_rvalue_reference_t, {type})));
detail::__underlying_entity_of(substitute(^^add_rvalue_reference_t,
{type})));
}
consteval auto make_signed(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^make_signed_t, {type})));
detail::__underlying_entity_of(substitute(^^make_signed_t, {type})));
}
consteval auto make_unsigned(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^make_unsigned_t, {type})));
detail::__underlying_entity_of(substitute(^^make_unsigned_t, {type})));
}
consteval auto remove_extent(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_extent_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_extent_t, {type})));
}
consteval auto remove_all_extents(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_all_extents_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_all_extents_t,
{type})));
}
consteval auto remove_pointer(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_pointer_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_pointer_t, {type})));
}
consteval auto add_pointer(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^add_pointer_t, {type})));
detail::__underlying_entity_of(substitute(^^add_pointer_t, {type})));
}
consteval auto remove_cvref(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^remove_cvref_t, {type})));
detail::__underlying_entity_of(substitute(^^remove_cvref_t, {type})));
}
consteval auto decay(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^decay_t, {type})));
detail::__underlying_entity_of(substitute(^^decay_t, {type})));
}
template <reflection_range R = initializer_list<info>>
consteval auto common_type(R &&args) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^common_type_t, args)));
detail::__underlying_entity_of(substitute(^^common_type_t, args)));
}
template <reflection_range R = initializer_list<info>>
consteval auto common_reference(R &&args) -> info {
return dealias(substitute(^^common_reference_t, args));
return detail::__underlying_entity_of(substitute(^^common_reference_t, args));
}
consteval auto underlying_type(info type) -> info {
return detail::__workaround_expand_compiler_builtins(
dealias(substitute(^^underlying_type_t, {type})));
detail::__underlying_entity_of(substitute(^^underlying_type_t, {type})));
}
template <reflection_range R = initializer_list<info>>
@@ -2007,15 +2029,17 @@ consteval auto invoke_result(info type, R &&args) -> info {
vector<info> targs(from_range, args);
targs.insert(targs.begin(), type);
return dealias(substitute(^^invoke_result_t, targs));
return detail::__underlying_entity_of(substitute(^^invoke_result_t, targs));
}
consteval auto unwrap_reference(info type) -> info {
return dealias(substitute(^^unwrap_reference_t, {type}));
return detail::__underlying_entity_of(substitute(^^unwrap_reference_t,
{type}));
}
consteval auto unwrap_ref_decay(info type) -> info {
return dealias(substitute(^^unwrap_ref_decay_t, {type}));
return detail::__underlying_entity_of(substitute(^^unwrap_ref_decay_t,
{type}));
}
consteval auto tuple_size(info type) -> size_t {
@@ -2023,7 +2047,7 @@ consteval auto tuple_size(info type) -> size_t {
}
consteval auto tuple_element(size_t index, info type) -> info {
return dealias(substitute(^^tuple_element_t,
return detail::__underlying_entity_of(substitute(^^tuple_element_t,
{reflect_value(index), type}));
}
@@ -2032,7 +2056,7 @@ consteval auto variant_size(info type) -> size_t {
}
consteval auto variant_alternative(size_t index, info type) -> info {
return dealias(substitute(^^variant_alternative_t,
return detail::__underlying_entity_of(substitute(^^variant_alternative_t,
{reflect_value(index), type}));
}
@@ -2041,7 +2065,8 @@ namespace detail {
template <class T> struct __wrap_workaround { using type = T; };
consteval auto __workaround_expand_compiler_builtins(info type) -> info {
auto r = substitute(^^__wrap_workaround, {type});
return dealias(members_of(r, access_context::unchecked())[0]);
r = members_of(r, access_context::unchecked())[0];
return detail::__underlying_entity_of(r);
}
} // namespace detail
@@ -2216,11 +2241,12 @@ template <typename Ty>
[[deprecated("separated into 'reflect_value', 'reflect_result', and "
"'reflect_function' in P2996R4")]]
consteval auto reflect_result(Ty r) -> info {
constexpr auto DTy = dealias(^^Ty);
constexpr auto DTy = detail::__underlying_entity_of(^^Ty);
constexpr auto RTy = is_class_v<[:DTy:]> || is_reference_v<[:DTy:]> ?
DTy : ^^remove_cv_t<[:DTy:]>;
return __metafunction(detail::__metafn_reflect_result, dealias(RTy),
return __metafunction(detail::__metafn_reflect_result,
detail::__underlying_entity_of(RTy),
static_cast<typename [:DTy:]>(r));
}
@@ -2357,8 +2383,8 @@ namespace detail {
template <typename CharT>
struct pretty_printer {
static_assert(dealias(^^CharT) == ^^char ||
dealias(^^CharT) == ^^char8_t);
static_assert(detail::__underlying_entity_of(^^CharT) == ^^char ||
detail::__underlying_entity_of(^^CharT) == ^^char8_t);
static constexpr bool IsUtf8 = is_same_type(^^CharT, ^^char8_t);
using string_t = basic_string<CharT>;
@@ -2588,7 +2614,7 @@ template <info R> // Values of fundamental type.
requires(is_value(R) && is_fundamental_type(type_of(R)))
consteval auto pretty_printer<CharT>::tprint_impl::render() -> string_t {
string_t result;
constexpr info Ty = dealias(type_of(R));
constexpr info Ty = detail::__underlying_entity_of(type_of(R));
if constexpr (Ty == ^^nullptr_t)
result = string_constant("nullptr");
@@ -2606,7 +2632,8 @@ consteval auto pretty_printer<CharT>::tprint_impl::render() -> string_t {
result += string_constant("'");
}
else if constexpr (is_integral_type(Ty) && dealias(^^CharT) == ^^char) {
else if constexpr (is_integral_type(Ty) &&
detail::__underlying_entity_of(^^CharT) == ^^char) {
CharT buffer[32] = {};
if (!to_chars(begin(buffer), end(buffer), [:R:]))
throw "'to_chars' failed to format integeral value";
@@ -2760,7 +2787,7 @@ consteval auto pretty_printer<CharT>::tprint_impl::render() -> string_t {
if (R == ^^decltype(nullptr) || R == ^^nullptr_t)
return string_constant("std::nullptr_t");
if (R == ^^info || R == dealias(^^info))
if (R == ^^info || R == detail::__underlying_entity_of(^^info))
return string_constant("std::meta::info");
if (R == ^^bool) return string_constant("bool");

View File

@@ -50,22 +50,23 @@ static_assert(is_accessible(^^B::Inner, ctx));
static_assert(is_entity_proxy(^^B::Inner));
static_assert(!is_entity_proxy(^^A::Inner));
static_assert(nonstatic_data_members_of(dealias(^^B::Inner), ctx).size() == 1);
static_assert(bases_of(dealias(^^B::Inner), ctx).size() == 1);
static_assert(nonstatic_data_members_of(underlying_entity_of(^^B::Inner),
ctx).size() == 1);
static_assert(bases_of(underlying_entity_of(^^B::Inner), ctx).size() == 1);
static_assert(template_arguments_of(dealias(^^B::Alias)) ==
static_assert(template_arguments_of(underlying_entity_of(^^B::Alias)) ==
std::vector {^^int, ^^std::allocator<int>});
static_assert(identifier_of(^^B::Alias) == "Alias");
static_assert(source_location_of(^^B::Alias).line() == 34);
static_assert(type_of(dealias(^^B::m)) == ^^int);
static_assert(type_of(underlying_entity_of(^^B::m)) == ^^int);
static_assert(parent_of(^^B::m) == ^^B);
static_assert(dealias(^^B::Alias) == ^^std::vector<int>);
static_assert(template_of(dealias(^^B::Alias)) == ^^std::vector);
static_assert(has_template_arguments(dealias(^^B::Alias)));
static_assert(underlying_entity_of(^^B::Alias) == ^^std::vector<int>);
static_assert(template_of(underlying_entity_of(^^B::Alias)) == ^^std::vector);
static_assert(has_template_arguments(underlying_entity_of(^^B::Alias)));
static_assert(!has_template_arguments(^^B::Alias));
static_assert(substitute(dealias(^^D::TCls), {^^D::Inner}) ==
static_assert(substitute(underlying_entity_of(^^D::TCls), {^^D::Inner}) ==
^^C::TCls<C::Inner>);
static_assert(identifier_of(members_of(^^D, unchecked)[2]) == "m");
@@ -74,7 +75,7 @@ static_assert(is_private(members_of(^^D, unchecked)[2]));
static_assert(!is_class_member(^^Enum::Red));
static_assert(is_class_member(^^E::Red));
static_assert(is_entity_proxy(^^E::Red));
static_assert(dealias(^^E::Red) == ^^Enum::Red);
static_assert(underlying_entity_of(^^E::Red) == ^^Enum::Red);
static_assert(!is_namespace_member(^^Enum::Red));
static_assert(is_namespace_member(^^InnerNS::Red));
@@ -83,7 +84,7 @@ static_assert(extract<int>(annotations_of(^^G::m)[0]) == 32);
static_assert(!is_access_specified(^^G::m));
static_assert(is_access_specified(^^G::n));
static_assert(dealias(^^H::n) == ^^F::n);
static_assert(underlying_entity_of(^^H::n) == ^^F::n);
// =================
// dependent_proxies
@@ -106,14 +107,14 @@ static_assert(is_entity_proxy(S<A>::r));
static_assert(is_entity_proxy(S<A>::s));
static_assert(parent_of(S<A>::r) == ^^S<A>);
static_assert(S<A>::r == S<A>::s);
static_assert(dealias(S<A>::r) == ^^A::fn);
static_assert(underlying_entity_of(S<A>::r) == ^^A::fn);
static_assert(&[:S<A>::r:] == &A::fn);
static_assert(is_entity_proxy(S<A>::t));
static_assert(is_entity_proxy(S<A>::u));
static_assert(parent_of(S<A>::t) == ^^S<A>);
static_assert(S<A>::t == S<A>::u);
static_assert(dealias(S<A>::t) == ^^A::Inner);
static_assert(underlying_entity_of(S<A>::t) == ^^A::Inner);
// ===========
// using_enums
@@ -125,12 +126,29 @@ struct S { using enum Color; };
static_assert(^^S::Red != ^^S::Green);
static_assert(^^S::Red != ^^Color::Red);
static_assert(dealias(^^S::Red) == ^^Color::Red);
static_assert(underlying_entity_of(^^S::Red) == ^^Color::Red);
static_assert(is_entity_proxy(^^S::Red));
static_assert([:^^S::Red:] == Color::Red);
} // namespace using_enums
// =============
// paper_example
// =============
namespace paper_example {
struct B { using Alias = int; };
struct D : B { using B::Alias; };
static_assert(is_type_alias(^^B::Alias));
static_assert(is_entity_proxy(^^D::Alias));
static_assert(^^B::Alias != ^^D::Alias);
static_assert(proxied_entity_of(^^D::Alias) == ^^B::Alias);
static_assert(underlying_entity_of(^^D::Alias) == ^^int);
static_assert(underlying_entity_of(^^B::Alias) == underlying_entity_of(^^D::Alias));
} // namespace paper_example
} // namespace dependent_proxies
int main() { }

View File

@@ -329,6 +329,7 @@ struct B1 {};
struct B2 {};
struct B3 {};
struct D1 : public B1, virtual protected B2, private B3 {};
using Alias = D1;
static_assert(bases_of(^^B1, access_context::unchecked()).size() == 0);
static_assert(type_of(bases_of(^^D1, access_context::unchecked())[0]) == ^^B1);
static_assert(type_of(bases_of(^^D1, access_context::unchecked())[1]) == ^^B2);
@@ -346,6 +347,9 @@ static_assert(!is_protected(bases_of(^^D1, access_context::unchecked())[2]));
static_assert(is_private(bases_of(^^D1, access_context::unchecked())[2]));
static_assert(!is_virtual(bases_of(^^D1, access_context::unchecked())[2]));
static_assert(type_of(bases_of(^^Alias,
access_context::unchecked())[0]) == ^^B1);
template <typename... Bases> struct D2 : Bases... {};
static_assert(type_of(bases_of(^^D2<B1, B3>, access_context::unchecked())[0]) ==
^^B1);

View File

@@ -70,6 +70,7 @@ static_assert((annotations_of(^^TCls<int>) |
std::meta::reflect_value(5),
std::meta::reflect_value(6),
std::meta::reflect_value(3.0f)});
static_assert((annotations_of(^^TFn<int>) |
std::views::transform(std::meta::value_of) |
std::ranges::to<std::vector>()) ==
@@ -251,4 +252,19 @@ static_assert(c3 == -1);
static_assert(c4 == 4);
} // namespace ledger_based_consteval_variable
// ===========================
// templated_class_annotations
// ===========================
namespace templated_class_annotations {
template <class T>
struct X {
struct [[=1]] C;
struct [[=2]] D { };
};
static_assert(annotations_of(^^X<int>::C).size() == 1);
static_assert(annotations_of(^^X<int>::D).size() == 1);
} // namespace templated_class_annotations
int main() { }

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() { }

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>
//

View File

@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// Copyright 2024 Bloomberg Finance L.P.
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20
// ADDITIONAL_COMPILE_FLAGS: -freflection
// <experimental/reflection>
//
// [reflection]
#include <experimental/meta>
// ==================
// disallowed_results
// ==================
namespace disallowed_results {
constexpr auto v1 = std::meta::reflect_value((const char *)"fails");
// expected-error@-1 {{must be initialized by a constant expression}} \
// expected-note@-1 {{provided value cannot be represented}}
struct HoldsTemporary {
const int &tmp;
};
constexpr HoldsTemporary htmp{42};
constexpr auto v2 = std::meta::reflect_value(htmp);
// expected-error@-1 {{must be initialized by a constant expression}} \
// expected-note@-1 {{provided value cannot be represented}}
} // namespace disallowed_results
int main() { }