Files
carbon-lang/common/struct_reflection.h
T
Richard SmithandJon Ross-Perkins 3c01ee69ed Move information on the token associated with a parse node from the .def file into the typed node. (#4001)
Instead of tracking the token associated with a parse node in the `.def`
file macro, track it on the typed node instead. List the token as a
field inside the node structure to show the order of the token relative
to the other components of the grammar production, and to allow the
token index to be accessed when the node is extracted.

Remove the corresponding information from the `.def` file, leaving
behind just a list of parse node kinds in the majority of cases.

This also removes the checking of the token kind associated with a parse
node in the case where the parse node has errors. Previously we had a
flag on the node kind to indicate whether we should check this, but per
[discord
discussion](https://discord.com/channels/655572317891461132/655578254970716160/1246214418979881052),
we have decided to remove this.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2024-05-31 23:11:51 +00:00

202 lines
6.3 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_COMMON_STRUCT_REFLECTION_H_
#define CARBON_COMMON_STRUCT_REFLECTION_H_
// Reflection support for simple struct types.
//
// Example usage:
//
// ```
// struct A { int x; std::string y; };
//
// A a;
// std::tuple<int, std::string> t = StructReflection::AsTuple(a);
// ```
//
// Limitations:
//
// - Only simple aggregate structs are supported. Types with base classes,
// non-public data members, constructors, or virtual functions are not
// supported.
// - Structs with more than 8 fields are not supported. This limit is easy to
// increase if needed, but removing it entirely is hard.
// - Structs containing a reference to the same type are not supported.
#include <tuple>
#include <type_traits>
namespace Carbon::StructReflection {
namespace Internal {
// A type that can be converted to any field type within type T.
template <typename T>
struct AnyField {
template <typename FieldT>
// NOLINTNEXTLINE(google-explicit-constructor)
operator FieldT&() const;
template <typename FieldT>
// NOLINTNEXTLINE(google-explicit-constructor)
operator FieldT&&() const;
// Don't allow conversion to T itself. This ensures we don't match against a
// copy or move constructor.
operator T&() const = delete;
operator T&&() const = delete;
};
// The detection mechanism below intentionally misses field initializers.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
// Detector for whether we can list-initialize T from the given list of fields.
template <typename T, typename... Fields>
constexpr auto CanListInitialize(decltype(T{Fields()...})* /*unused*/) -> bool {
return true;
}
template <typename T, typename... Fields>
constexpr auto CanListInitialize(...) -> bool {
return false;
}
#pragma clang diagnostic pop
// Simple detector to find the number of data fields in a struct. This proceeds
// in two passes:
//
// 1) Add AnyField<T>s until we can initialize T from our list of initializers.
// 2) Add more AnyField<T>s until we can't initialize any more.
template <typename T, bool AnyWorkedSoFar = false, typename... Fields>
constexpr auto CountFields() -> int {
if constexpr (CanListInitialize<T, Fields...>(0)) {
return CountFields<T, true, Fields..., AnyField<T>>();
} else if constexpr (AnyWorkedSoFar) {
constexpr int NumFields = sizeof...(Fields) - 1;
static_assert(NumFields <= 8, "Unsupported: too many fields in struct");
return NumFields;
} else if constexpr (sizeof...(Fields) > 32) {
// If we go too far without finding a working initializer, something
// probably went wrong with our calculation. Bail out before we recurse too
// deeply.
static_assert(sizeof...(Fields) <= 32,
"Internal error, could not count fields in struct");
} else {
return CountFields<T, false, Fields..., AnyField<T>>();
}
}
// Utility to access fields by index.
template <int NumFields>
struct FieldAccessor;
template <>
struct FieldAccessor<0> {
template <typename T>
static auto Get(T& /*value*/) -> auto {
return std::tuple<>();
}
};
template <>
struct FieldAccessor<1> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0] = value;
return std::tuple<decltype(field0)>(field0);
}
};
template <>
struct FieldAccessor<2> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1] = value;
return std::tuple<decltype(field0), decltype(field1)>(field0, field1);
}
};
template <>
struct FieldAccessor<3> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2] = value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2)>(
field0, field1, field2);
}
};
template <>
struct FieldAccessor<4> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2, field3] = value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
decltype(field3)>(field0, field1, field2, field3);
}
};
template <>
struct FieldAccessor<5> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2, field3, field4] = value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
decltype(field3), decltype(field4)>(
field0, field1, field2, field3, field4);
}
};
template <>
struct FieldAccessor<6> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2, field3, field4, field5] = value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
decltype(field3), decltype(field4), decltype(field5)>(
field0, field1, field2, field3, field4, field5);
}
};
template <>
struct FieldAccessor<7> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2, field3, field4, field5, field6] = value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
decltype(field3), decltype(field4), decltype(field5),
decltype(field6)>(field0, field1, field2, field3, field4,
field5, field6);
}
};
template <>
struct FieldAccessor<8> {
template <typename T>
static auto Get(T& value) -> auto {
auto& [field0, field1, field2, field3, field4, field5, field6, field7] =
value;
return std::tuple<decltype(field0), decltype(field1), decltype(field2),
decltype(field3), decltype(field4), decltype(field5),
decltype(field6), decltype(field7)>(
field0, field1, field2, field3, field4, field5, field6, field7);
}
};
} // namespace Internal
// Get the fields of the struct `T` as a tuple.
template <typename T>
auto AsTuple(T value) -> auto {
// We use aggregate initialization to detect the number of fields.
static_assert(std::is_aggregate_v<T>, "Only aggregates are supported");
return Internal::FieldAccessor<Internal::CountFields<T>()>::Get(value);
}
} // namespace Carbon::StructReflection
#endif // CARBON_COMMON_STRUCT_REFLECTION_H_