[llvm] 65a3712 - [yamlio] Allow parsing an entire mapping as an enumeration
via llvm-commits
llvm-commits at lists.llvm.org
Sun Mar 13 21:47:37 PDT 2022
Author: sstwcw
Date: 2022-03-14T04:41:40Z
New Revision: 65a3712af66fb0abfdce96a1f86ae5bad9a48b1c
URL: https://github.com/llvm/llvm-project/commit/65a3712af66fb0abfdce96a1f86ae5bad9a48b1c
DIFF: https://github.com/llvm/llvm-project/commit/65a3712af66fb0abfdce96a1f86ae5bad9a48b1c.diff
LOG: [yamlio] Allow parsing an entire mapping as an enumeration
For when we want to change a configuration option from an enum into a
struct. The need arose when working on D119599.
Reviewed By: jhenderson
Differential Revision: https://reviews.llvm.org/D120363
Added:
Modified:
llvm/docs/YamlIO.rst
llvm/include/llvm/Support/YAMLTraits.h
llvm/unittests/Support/YAMLIOTest.cpp
Removed:
################################################################################
diff --git a/llvm/docs/YamlIO.rst b/llvm/docs/YamlIO.rst
index c696affb6c182..7137c56ebb037 100644
--- a/llvm/docs/YamlIO.rst
+++ b/llvm/docs/YamlIO.rst
@@ -551,6 +551,34 @@ you can specialize on the class pointer. Examples:
}
};
+There are circumstances where we want to allow the entire mapping to be
+read as an enumeration. For example, say some configuration option
+started as an enumeration. Then it got more complex so it is now a
+mapping. But it is necessary to support the old configuration files.
+In that case, add a function ``enumInput`` like for
+``ScalarEnumerationTraits::enumeration``. Examples:
+
+.. code-block:: c++
+
+ struct FooBarEnum {
+ int Foo;
+ int Bar;
+ bool operator==(const FooBarEnum &R) const {
+ return Foo == R.Foo && Bar == R.Bar;
+ }
+ };
+
+ template <> struct MappingTraits<FooBarEnum> {
+ static void enumInput(IO &io, FooBarEnum &Val) {
+ io.enumCase(Val, "OnlyFoo", FooBarEnum({1, 0}));
+ io.enumCase(Val, "OnlyBar", FooBarEnum({0, 1}));
+ }
+ static void mapping(IO &io, FooBarEnum &Val) {
+ io.mapOptional("Foo", Val.Foo);
+ io.mapOptional("Bar", Val.Bar);
+ }
+ };
+
No Normalization
----------------
diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h
index 0bc8bbc8743a9..34286dc44e978 100644
--- a/llvm/include/llvm/Support/YAMLTraits.h
+++ b/llvm/include/llvm/Support/YAMLTraits.h
@@ -62,6 +62,7 @@ struct MappingTraits {
// static void mapping(IO &io, T &fields);
// Optionally may provide:
// static std::string validate(IO &io, T &fields);
+ // static void enumInput(IO &io, T &value);
//
// The optional flow flag will cause generated YAML to use a flow mapping
// (e.g. { a: 0, b: 1 }):
@@ -445,6 +446,31 @@ template <class T> struct has_MappingValidateTraits<T, EmptyContext> {
static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
};
+// Test if MappingContextTraits<T>::enumInput() is defined on type T.
+template <class T, class Context> struct has_MappingEnumInputTraits {
+ using Signature_validate = void (*)(class IO &, T &);
+
+ template <typename U>
+ static char test(SameType<Signature_validate, &U::enumInput> *);
+
+ template <typename U> static double test(...);
+
+ static bool const value =
+ (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1);
+};
+
+// Test if MappingTraits<T>::enumInput() is defined on type T.
+template <class T> struct has_MappingEnumInputTraits<T, EmptyContext> {
+ using Signature_validate = void (*)(class IO &, T &);
+
+ template <typename U>
+ static char test(SameType<Signature_validate, &U::enumInput> *);
+
+ template <typename U> static double test(...);
+
+ static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1);
+};
+
// Test if SequenceTraits<T> is defined on type T.
template <class T>
struct has_SequenceMethodTraits
@@ -1058,9 +1084,30 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
io.endMapping();
}
+template <typename T, typename Context>
+std::enable_if_t<!has_MappingEnumInputTraits<T, Context>::value, bool>
+yamlizeMappingEnumInput(IO &io, T &Val) {
+ return false;
+}
+
+template <typename T, typename Context>
+std::enable_if_t<has_MappingEnumInputTraits<T, Context>::value, bool>
+yamlizeMappingEnumInput(IO &io, T &Val) {
+ if (io.outputting())
+ return false;
+
+ io.beginEnumScalar();
+ MappingTraits<T>::enumInput(io, Val);
+ bool Matched = !io.matchEnumFallback();
+ io.endEnumScalar();
+ return Matched;
+}
+
template <typename T, typename Context>
std::enable_if_t<unvalidatedMappingTraits<T, Context>::value, void>
yamlize(IO &io, T &Val, bool, Context &Ctx) {
+ if (yamlizeMappingEnumInput<T, Context>(io, Val))
+ return;
if (has_FlowTraits<MappingTraits<T>>::value) {
io.beginFlowMapping();
detail::doMapping(io, Val, Ctx);
diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp
index a9230c2ca4767..ae676c73970e8 100644
--- a/llvm/unittests/Support/YAMLIOTest.cpp
+++ b/llvm/unittests/Support/YAMLIOTest.cpp
@@ -238,6 +238,58 @@ TEST(YAMLIO, TestSequenceMapWriteAndRead) {
}
}
+//
+// Test reading the entire struct as an enum.
+//
+
+struct FooBarEnum {
+ int Foo;
+ int Bar;
+ bool operator==(const FooBarEnum &R) const {
+ return Foo == R.Foo && Bar == R.Bar;
+ }
+};
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<FooBarEnum> {
+ static void enumInput(IO &io, FooBarEnum &Val) {
+ io.enumCase(Val, "OnlyFoo", FooBarEnum({1, 0}));
+ io.enumCase(Val, "OnlyBar", FooBarEnum({0, 1}));
+ }
+ static void mapping(IO &io, FooBarEnum &Val) {
+ io.mapOptional("Foo", Val.Foo);
+ io.mapOptional("Bar", Val.Bar);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
+TEST(YAMLIO, TestMapEnumRead) {
+ FooBarEnum Doc;
+ {
+ Input Yin("OnlyFoo");
+ Yin >> Doc;
+ EXPECT_FALSE(Yin.error());
+ EXPECT_EQ(Doc.Foo, 1);
+ EXPECT_EQ(Doc.Bar, 0);
+ }
+ {
+ Input Yin("OnlyBar");
+ Yin >> Doc;
+ EXPECT_FALSE(Yin.error());
+ EXPECT_EQ(Doc.Foo, 0);
+ EXPECT_EQ(Doc.Bar, 1);
+ }
+ {
+ Input Yin("{Foo: 3, Bar: 5}");
+ Yin >> Doc;
+ EXPECT_FALSE(Yin.error());
+ EXPECT_EQ(Doc.Foo, 3);
+ EXPECT_EQ(Doc.Bar, 5);
+ }
+}
+
//
// Test YAML filename handling.
//
More information about the llvm-commits
mailing list