Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
400 changes: 400 additions & 0 deletions llvm/include/llvm/Support/YAMLGenerateSchema.h

Large diffs are not rendered by default.

71 changes: 46 additions & 25 deletions llvm/include/llvm/Support/YAMLTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -683,12 +683,19 @@ struct unvalidatedMappingTraits
!has_MappingValidateTraits<T, Context>::value> {
};

enum class IOKind : uint8_t {
Outputting,
Inputting,
GeneratingSchema,
};

// Base class for Input and Output.
class LLVM_ABI IO {
public:
IO(void *Ctxt = nullptr);
virtual ~IO();

virtual IOKind getKind() const = 0;
virtual bool outputting() const = 0;

virtual unsigned beginSequence() = 0;
Expand Down Expand Up @@ -732,15 +739,17 @@ class LLVM_ABI IO {
virtual void setAllowUnknownKeys(bool Allow);

template <typename T> void enumCase(T &Val, StringRef Str, const T ConstVal) {
if (matchEnumScalar(Str, outputting() && Val == ConstVal)) {
if (matchEnumScalar(Str,
getKind() == IOKind::Outputting && Val == ConstVal)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this and similar code need to change rather than continue to use outputting()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is because generate schema should behave like Outputting in some cases and like Inputting in others. And in such a case I had to explicitly specify these places, implying that !IOKind::Inputting == (IOKind::Outputting || IOKind::GeneratingSchema). I also decided to leave virtual method outputting() not to break backward compatibility as it is potentially can be called in IO class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also decided to leave virtual method outputting() not to break backward compatibility as it is potentially can be called in IO class.

I don't agree that this maintains backward compatibility, at least not in a meaningful way. The fact that IOKind is needed for GenerateSchema itself implies that uses of IO exist which are broken when relying on just outputting(), right?

In looking over the code (at least the latest version) I worry that the complexity is growing to the point that the current code sharing approach (a base class with conditions) is not the best approach.

Val = ConstVal;
}
}

// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
template <typename T>
void enumCase(T &Val, StringRef Str, const uint32_t ConstVal) {
if (matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal))) {
if (matchEnumScalar(Str, getKind() == IOKind::Outputting &&
Val == static_cast<T>(ConstVal))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be good idea to run Clang-Format. Same in other similar places.

Copy link
Contributor Author

@tgs-sc tgs-sc Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I can not run clang-tidy on the whole file because it is randomly formatted for now (as it is very ancient). But I can run clang-format on the whole file and ask you to merge these changes in separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about clang-format-diff.py?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran clang-format-diff.py, but it didn't change this part (changed some other places)

Val = ConstVal;
}
}
Expand All @@ -757,29 +766,33 @@ class LLVM_ABI IO {

template <typename T>
void bitSetCase(T &Val, StringRef Str, const T ConstVal) {
if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) {
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
(Val & ConstVal) == ConstVal)) {
Val = static_cast<T>(Val | ConstVal);
}
}

// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
template <typename T>
void bitSetCase(T &Val, StringRef Str, const uint32_t ConstVal) {
if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) {
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
(Val & ConstVal) == ConstVal)) {
Val = static_cast<T>(Val | ConstVal);
}
}

template <typename T>
void maskedBitSetCase(T &Val, StringRef Str, T ConstVal, T Mask) {
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
(Val & Mask) == ConstVal))
Val = Val | ConstVal;
}

template <typename T>
void maskedBitSetCase(T &Val, StringRef Str, uint32_t ConstVal,
uint32_t Mask) {
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
(Val & Mask) == ConstVal))
Val = Val | ConstVal;
}

Expand Down Expand Up @@ -844,7 +857,8 @@ class LLVM_ABI IO {
bool Required, Context &Ctx) {
void *SaveInfo;
bool UseDefault;
const bool sameAsDefault = outputting() && Val == DefaultValue;
const bool sameAsDefault =
(getKind() == IOKind::Outputting) && Val == DefaultValue;
if (this->preflightKey(Key, Required, sameAsDefault, UseDefault,
SaveInfo)) {
yamlize(*this, Val, Required, Ctx);
Expand Down Expand Up @@ -905,7 +919,7 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
template <typename T>
std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
EmptyContext &Ctx) {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
SmallString<128> Storage;
raw_svector_ostream Buffer(Storage);
ScalarTraits<T>::output(Val, io.getContext(), Buffer);
Expand All @@ -924,7 +938,7 @@ std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
template <typename T>
std::enable_if_t<has_BlockScalarTraits<T>::value, void>
yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
if (YamlIO.outputting()) {
if (YamlIO.getKind() == IOKind::Outputting) {
std::string Storage;
raw_string_ostream Buffer(Storage);
BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer);
Expand All @@ -943,7 +957,7 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
template <typename T>
std::enable_if_t<has_TaggedScalarTraits<T>::value, void>
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
std::string ScalarStorage, TagStorage;
raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage);
TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer,
Expand Down Expand Up @@ -985,15 +999,15 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
io.beginFlowMapping();
else
io.beginMapping();
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
std::string Err = detail::doValidate(io, Val, Ctx);
if (!Err.empty()) {
errs() << Err << "\n";
assert(Err.empty() && "invalid struct trying to be written as yaml");
}
}
detail::doMapping(io, Val, Ctx);
if (!io.outputting()) {
if (io.getKind() == IOKind::Inputting) {
std::string Err = detail::doValidate(io, Val, Ctx);
if (!Err.empty())
io.setError(Err);
Expand All @@ -1007,7 +1021,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
template <typename T, typename Context>
bool yamlizeMappingEnumInput(IO &io, T &Val) {
if constexpr (has_MappingEnumInputTraits<T, Context>::value) {
if (io.outputting())
if (io.getKind() == IOKind::Outputting)
return false;

io.beginEnumScalar();
Expand Down Expand Up @@ -1038,7 +1052,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
template <typename T>
std::enable_if_t<has_CustomMappingTraits<T>::value, void>
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
io.beginMapping();
CustomMappingTraits<T>::output(io, Val);
io.endMapping();
Expand All @@ -1053,8 +1067,9 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
template <typename T>
std::enable_if_t<has_PolymorphicTraits<T>::value, void>
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val)
: io.getNodeKind()) {
switch (io.getKind() == IOKind::Outputting
? PolymorphicTraits<T>::getKind(Val)
: io.getNodeKind()) {
case NodeKind::Scalar:
return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx);
case NodeKind::Map:
Expand All @@ -1075,7 +1090,9 @@ std::enable_if_t<has_SequenceTraits<T>::value, void>
yamlize(IO &io, T &Seq, bool, Context &Ctx) {
if (has_FlowTraits<SequenceTraits<T>>::value) {
unsigned incnt = io.beginFlowSequence();
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
unsigned count = io.getKind() == IOKind::Outputting
? SequenceTraits<T>::size(io, Seq)
: incnt;
for (unsigned i = 0; i < count; ++i) {
void *SaveInfo;
if (io.preflightFlowElement(i, SaveInfo)) {
Expand All @@ -1086,7 +1103,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) {
io.endFlowSequence();
} else {
unsigned incnt = io.beginSequence();
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
unsigned count = io.getKind() == IOKind::Outputting
? SequenceTraits<T>::size(io, Seq)
: incnt;
for (unsigned i = 0; i < count; ++i) {
void *SaveInfo;
if (io.preflightElement(i, SaveInfo)) {
Expand Down Expand Up @@ -1239,15 +1258,15 @@ struct ScalarBitSetTraits<
template <typename TNorm, typename TFinal> struct MappingNormalization {
MappingNormalization(IO &i_o, TFinal &Obj)
: io(i_o), BufPtr(nullptr), Result(Obj) {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
BufPtr = new (&Buffer) TNorm(io, Obj);
} else {
BufPtr = new (&Buffer) TNorm(io);
}
}

~MappingNormalization() {
if (!io.outputting()) {
if (io.getKind() != IOKind::Outputting) {
Result = BufPtr->denormalize(io);
}
BufPtr->~TNorm();
Expand All @@ -1269,7 +1288,7 @@ template <typename TNorm, typename TFinal> struct MappingNormalization {
template <typename TNorm, typename TFinal> struct MappingNormalizationHeap {
MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator)
: io(i_o), Result(Obj) {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
BufPtr = new (&Buffer) TNorm(io, Obj);
} else if (allocator) {
BufPtr = allocator->Allocate<TNorm>();
Expand All @@ -1280,7 +1299,7 @@ template <typename TNorm, typename TFinal> struct MappingNormalizationHeap {
}

~MappingNormalizationHeap() {
if (io.outputting()) {
if (io.getKind() == IOKind::Outputting) {
BufPtr->~TNorm();
} else {
Result = BufPtr->denormalize(io);
Expand Down Expand Up @@ -1327,6 +1346,7 @@ class LLVM_ABI Input : public IO {
std::error_code error() override;

private:
IOKind getKind() const override;
bool outputting() const override;
bool mapTag(StringRef, bool) override;
void beginMapping() override;
Expand Down Expand Up @@ -1478,6 +1498,7 @@ class LLVM_ABI Output : public IO {
/// anyway.
void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; }

IOKind getKind() const override;
bool outputting() const override;
bool mapTag(StringRef, bool) override;
void beginMapping() override;
Expand Down Expand Up @@ -1563,8 +1584,8 @@ void IO::processKeyWithDefault(StringRef Key, std::optional<T> &Val,
assert(!DefaultValue && "std::optional<T> shouldn't have a value!");
void *SaveInfo;
bool UseDefault = true;
const bool sameAsDefault = outputting() && !Val;
if (!outputting() && !Val)
const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val;
if (getKind() != IOKind::Outputting && !Val)
Val = T();
if (Val &&
this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) {
Expand All @@ -1574,7 +1595,7 @@ void IO::processKeyWithDefault(StringRef Key, std::optional<T> &Val,
// was requested, i.e. the DefaultValue will be assigned. The DefaultValue
// is usually None.
bool IsNone = false;
if (!outputting())
if (getKind() == IOKind::Inputting)
if (const auto *Node =
dyn_cast<ScalarNode>(((Input *)this)->getCurrentNode()))
// We use rtrim to ignore possible white spaces that might exist when a
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ add_llvm_component_library(LLVMSupport
WithColor.cpp
YAMLParser.cpp
YAMLTraits.cpp
YAMLGenerateSchema.cpp
raw_os_ostream.cpp
raw_ostream.cpp
raw_ostream_proxy.cpp
Expand Down
Loading
Loading