[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