Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0f49155
Add reflection_hash
fullptr Apr 11, 2025
c48592e
Rename the struct and stub out the implementation
Aug 1, 2025
6a331de
Add in the branches for namespace reflections
Aug 1, 2025
fbf196a
Implement namespace hashing
Aug 1, 2025
0a81af2
Rename var
Aug 1, 2025
5de7689
Add braces to the branches to make them easy to add to
Aug 1, 2025
3e87faf
Implement reflection of types
Aug 4, 2025
efa7faf
Factor out appending the QualType functionality
Aug 4, 2025
43eb5a0
Implement hash for DataMemberSpec
Aug 4, 2025
0c0f392
Remove test code
Aug 4, 2025
ac08b85
Stub out the delcaration branch
Aug 4, 2025
864ed29
WIP
fullptr Aug 11, 2025
6850e1e
Reimplement consteval_hash
fullptr Aug 11, 2025
ca72923
Implement hashing for Parameter kind
fullptr Aug 12, 2025
a93f735
Implement hashing reflections of complex ints, complex floats and vec…
fullptr Aug 25, 2025
6554055
Implement FixedPoint reflections
fullptr Aug 25, 2025
aed977d
Implement hashing APValue::Array and figure out how to do struct and …
fullptr Aug 25, 2025
6d435a0
Implement hashing APValue::Struct
fullptr Aug 25, 2025
13420f2
Implement MemberPointer hashing
fullptr Aug 26, 2025
ad7ecb7
Implement hashing unions
fullptr Aug 26, 2025
3f79912
Implement hashing APValue::Reflection
fullptr Aug 26, 2025
0e2ba6b
AddLabelDiff is not possible
fullptr Aug 26, 2025
ea6244b
Implement reflecting nullptr
fullptr Aug 26, 2025
e57b76f
Add comment
fullptr Aug 26, 2025
65a29fa
Cleanup
fullptr Aug 26, 2025
c6f4f7f
Remove old comment
fullptr Aug 26, 2025
b2b9510
Formatting
fullptr Aug 26, 2025
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
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,13 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
#include "clang/AST/TypeNodes.inc"
};

private:
static int s_idCounter;
int TypeUniqueId = ++s_idCounter;

public:
auto getTypeUniqueId() const -> int { return TypeUniqueId; }

private:
/// Bitfields required by the Type class.
class TypeBitfields {
Expand Down
225 changes: 225 additions & 0 deletions clang/lib/AST/ExprConstantMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,15 @@ static bool reflect_invoke(APValue &Result, ASTContext &C, MetaActions &Meta,
SourceRange Range, ArrayRef<Expr *> Args,
Decl *ContainingDecl);

// ==========================================================
// std::consteval_hash<std::meta::info> specialization helper
// ==========================================================

static bool reflection_hash(APValue &Result, ASTContext &C, MetaActions &Meta,
EvalFn Evaluator, DiagFn Diagnoser, bool AllowInjection,
QualType ResultTy, SourceRange Range,
ArrayRef<Expr *> Args, Decl *ContainingDecl);

// -----------------------------------------------------------------------------
// Metafunction table
//
Expand Down Expand Up @@ -844,6 +853,9 @@ static constexpr Metafunction Metafunctions[] = {
// Other bespoke functions (not proposed at this time)
{ Metafunction::MFRK_bool, 1, 1, is_access_specified },
{ Metafunction::MFRK_metaInfo, 5, 5, reflect_invoke },

// std::consteval_hash<std::meta::info> specialization helper
{ Metafunction::MFRK_sizeT, 1, 1, reflection_hash },
};
constexpr const unsigned NumMetafunctions = sizeof(Metafunctions) /
sizeof(Metafunction);
Expand Down Expand Up @@ -1664,6 +1676,7 @@ StringRef DescriptionOf(APValue RV, bool Granular = true) {
return "an annotation";
}
}
return "unknown reflection";
}

bool DiagnoseReflectionKind(DiagFn Diagnoser, SourceRange Range,
Expand Down Expand Up @@ -6405,4 +6418,216 @@ bool reflect_invoke(APValue &Result, ASTContext &C, MetaActions &Meta,
return SetAndSucceed(Result, EvalResult.Val.Lift(CallExpr->getType()));
}

static void AppendQualType(llvm::FoldingSetNodeID &ID, const QualType &Ty)
{
ID.AddInteger(Ty->getTypeUniqueId());
ID.AddInteger(Ty.getQualifiers().getAsOpaqueValue());
}

static void AppendSourceLocation(llvm::FoldingSetNodeID &ID, const SourceLocation& Loc)
{
ID.AddInteger(Loc.getHashValue());
}

static void AppendSourceRange(llvm::FoldingSetNodeID &ID, const SourceRange& Range)
{
AppendSourceLocation(ID, Range.getBegin());
AppendSourceLocation(ID, Range.getEnd());
}

static void AppendLValue(ASTContext &C, llvm::FoldingSetNodeID &ID, const APValue &APV) {
const auto &Base = APV.getLValueBase();

if (!Base.is<const ValueDecl *>()) {
llvm_unreachable("can only reflect value decls");
}

const ValueDecl *VD = Base.get<const ValueDecl *>();
if (!VD) {
ID.AddInteger(0); // nullptr;
return;
}

ID.AddString(VD->getQualifiedNameAsString());
AppendQualType(ID, VD->getType());
ID.AddInteger(VD->getKind());

// Are these needed?
ID.AddInteger(APV.getLValueOffset().getQuantity());
ID.AddBoolean(APV.getLValueBase().isNull());
ID.AddBoolean(APV.isLValueOnePastTheEnd());
}

static void AppendReflection(ASTContext &C, llvm::FoldingSetNodeID &ID, const APValue& APV);
static void AppendAPValue(ASTContext &C, llvm::FoldingSetNodeID &ID, const APValue& APV)
{
ID.AddInteger(APV.getKind());
switch (APV.getKind()) {
case APValue::None:
case APValue::Indeterminate: {
llvm_unreachable("Type of APValue not available at compile time");
} break;
case APValue::Int: {
APV.getInt().Profile(ID);
} break;
case APValue::Float: {
APV.getFloat().Profile(ID);
} break;
case APValue::FixedPoint: {
APV.getFixedPoint().getValue().Profile(ID);
} break;
case APValue::ComplexInt: {
APV.getComplexIntImag().Profile(ID);
APV.getComplexIntReal().Profile(ID);
} break;
case APValue::ComplexFloat: {
APV.getComplexFloatImag().Profile(ID);
APV.getComplexFloatReal().Profile(ID);
} break;
case APValue::LValue: {
AppendLValue(C, ID, APV);
} break;
case APValue::Vector: {
for (std::size_t i = 0; i != APV.getVectorLength(); ++i) {
AppendAPValue(C, ID, APV.getVectorElt(i));
}
} break;

// Value is obtained by recursing and hashing the inner values.
case APValue::Array: {
for (std::size_t i = 0; i != APV.getArraySize(); ++i) {
if (i < APV.getArrayInitializedElts()) {
AppendAPValue(C, ID, APV.getArrayInitializedElt(i));
} else {
AppendAPValue(C, ID, APV.getArrayFiller());
}
}
} break;

// Should this be unique per type?
// Currently, given struct F { int x, y; }; and struct G { int x, y; };
// objects of these types with the same values for x and y hash to the same.
case APValue::Struct: {
for (std::size_t i = 0; i != APV.getStructNumFields(); ++i) {
AppendAPValue(C, ID, APV.getStructField(i));
}
for (std::size_t i = 0; i != APV.getStructNumBases(); ++i) {
AppendAPValue(C, ID, APV.getStructBase(i));
}
} break;

// Hash is based on the Union QualType as well as the name and value of the field.
case APValue::Union: {
const auto *ActiveField = APV.getUnionField();
ID.AddString(ActiveField->getName());
AppendQualType(ID, ActiveField->getType());
AppendQualType(ID, C.getRecordType(ActiveField->getParent()));
AppendAPValue(C, ID, APV.getUnionValue());
} break;

// Hash is based on the QualType of the struct that the pointer is a member of,
// and the name of the field.
case APValue::MemberPointer: {
auto* VD = APV.getMemberPointerDecl();
ID.AddString(VD->getNameAsString());
const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext());
AppendQualType(ID, C.getRecordType(RD));
} break;

// I believe this is only usable in C, so we cannot hope to get a
// reflection of it.
case APValue::AddrLabelDiff: {
llvm_unreachable("Non-standard extension not supported in C++");
} break;

case APValue::Reflection: {
auto V = APV;
while (V.getReflectionDepth() > 0) {
ID.AddInteger(V.getReflectionDepth());
V = V.Lower();
}
AppendReflection(C, ID, V);
} break;
default: {
llvm_unreachable("unknown ap value");
}
}
}

void AppendReflection(ASTContext &C, llvm::FoldingSetNodeID &ID, const APValue& APV)
{
ID.AddInteger(static_cast<std::size_t>(APV.getReflectionKind()));

switch (APV.getReflectionKind()) {
case ReflectionKind::Null: {
ID.AddInteger(0);
} break;
case ReflectionKind::Type: {
AppendQualType(ID, APV.getReflectedType());
} break;
case ReflectionKind::Object: {
AppendAPValue(C, ID, APV.getReflectedObject());
} break;
case ReflectionKind::Value: {
AppendAPValue(C, ID, APV.getReflectedValue());
} break;
case ReflectionKind::Declaration: {
AppendSourceRange(ID, APV.getReflectedDecl()->getSourceRange());
} break;
case ReflectionKind::Template: {
AppendSourceRange(ID, APV.getReflectedTemplate().getAsTemplateDecl()->getSourceRange());
} break;
case ReflectionKind::Namespace: {
AppendSourceRange(ID, APV.getReflectedNamespace()->getSourceRange());
} break;
case ReflectionKind::EntityProxy: {
AppendSourceLocation(ID, APV.getReflectedEntityProxy()->getLocation());
} break;
case ReflectionKind::Parameter: {
AppendSourceLocation(ID, APV.getReflectedParameter()->getLocation());
} break;
case ReflectionKind::BaseSpecifier: {
AppendSourceRange(ID, APV.getReflectedBaseSpecifier()->getSourceRange());
} break;
case ReflectionKind::DataMemberSpec: {
TagDataMemberSpec *TDMS = APV.getReflectedDataMemberSpec();
AppendQualType(ID, TDMS->Ty);
if (TDMS->Name) {
ID.AddString(TDMS->Name.value());
}
if (TDMS->Alignment) {
ID.AddInteger(TDMS->Alignment.value());
}
if (TDMS->BitWidth) {
ID.AddInteger(TDMS->BitWidth.value());
}
ID.AddInteger(TDMS->NoUniqueAddress);
} break;
case ReflectionKind::Annotation: {
AppendSourceLocation(ID, APV.getReflectedAnnotation()->getEqLoc());
} break;
default:
llvm_unreachable("unknown reflection kind");
}
}

bool reflection_hash(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.getSizeType());

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

llvm::FoldingSetNodeID ID;
AppendReflection(C, ID, R);
return SetAndSucceed(
Result,
APValue(C.MakeIntValue(ID.computeStableHash(), C.getSizeType())));
}

} // end namespace clang
2 changes: 2 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5719,3 +5719,5 @@ HLSLAttributedResourceType::findHandleTypeOnResource(const Type *RT) {
}
return nullptr;
}

int Type::s_idCounter = 0;
23 changes: 23 additions & 0 deletions libcxx/include/meta
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ enum : unsigned {
// Other bespoke functions (not proposed at this time)
__metafn_is_access_specified,
__metafn_reflect_invoke,

__metafn_reflection_hash,
};

consteval auto __workaround_expand_compiler_builtins(info type) -> info;
Expand Down Expand Up @@ -3023,6 +3025,27 @@ consteval auto u8display_string_of(info R) -> u8string_view {

_LIBCPP_END_NAMESPACE_REFLECTION_V2

_LIBCPP_BEGIN_NAMESPACE_STD

template <typename T> struct consteval_hash;

template <>
struct consteval_hash<meta::info>
{
consteval consteval_hash() = default;
consteval consteval_hash(const consteval_hash&) = default;
consteval consteval_hash(consteval_hash&&) = default;
consteval auto operator()(meta::info r) const noexcept -> size_t
{
return __metafunction(meta::detail::__metafn_reflection_hash, r);
}

private:
const meta::info unused = ^^::; // required to make type consteval-only
};

_LIBCPP_END_NAMESPACE_STD

#endif // __has_feature(reflection)

#endif // _LIBCPP_META