summaryrefslogtreecommitdiffstats
path: root/llvm/unittests/Support
diff options
context:
space:
mode:
authorScott Linder <scott@scottlinder.com>2018-11-14 19:39:59 +0000
committerScott Linder <scott@scottlinder.com>2018-11-14 19:39:59 +0000
commitc0830f55779adf07809f6b1af4351c3a2f293dfc (patch)
tree2e0903f2f91d399847025dea59473c66baf26b9d /llvm/unittests/Support
parent32dc5b9bf134b5a98c1a22395a5cd0ca5c409fe7 (diff)
downloadbcm5719-llvm-c0830f55779adf07809f6b1af4351c3a2f293dfc.tar.gz
bcm5719-llvm-c0830f55779adf07809f6b1af4351c3a2f293dfc.zip
[Support] Teach YAMLIO about polymorphic types
Add support for "polymorphic" types to YAMLIO. PolymorphicTraits can dynamically switch between other traits (Scalar, Map, or Sequence). When inputting, the PolymorphicTraits type is told which type to become, and when outputting the PolymorphicTraits type is asked which type it currently is. Also add support for TaggedScalarTraits to allow dynamically differentiating between multiple scalar types using YAML tags. Serialize empty maps as "{}" and empty sequences as "[]", so that types are preserved when round-tripping PolymorphicTraits. This change has equivalent semantics, but may break e.g. tests which compare output verbatim. Differential Revision: https://reviews.llvm.org/D48144 llvm-svn: 346884
Diffstat (limited to 'llvm/unittests/Support')
-rw-r--r--llvm/unittests/Support/YAMLIOTest.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp
index 94e9874147f..17f38f9b86c 100644
--- a/llvm/unittests/Support/YAMLIOTest.cpp
+++ b/llvm/unittests/Support/YAMLIOTest.cpp
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
@@ -2642,3 +2643,235 @@ TEST(YAMLIO, Numeric) {
EXPECT_FALSE(isNumeric("-inf"));
EXPECT_FALSE(isNumeric("1,230.15"));
}
+
+//===----------------------------------------------------------------------===//
+// Test PolymorphicTraits and TaggedScalarTraits
+//===----------------------------------------------------------------------===//
+
+struct Poly {
+ enum NodeKind {
+ NK_Scalar,
+ NK_Seq,
+ NK_Map,
+ } Kind;
+
+ Poly(NodeKind Kind) : Kind(Kind) {}
+
+ virtual ~Poly() = default;
+
+ NodeKind getKind() const { return Kind; }
+};
+
+struct Scalar : Poly {
+ enum ScalarKind {
+ SK_Unknown,
+ SK_Double,
+ SK_Bool,
+ } SKind;
+
+ union {
+ double DoubleValue;
+ bool BoolValue;
+ };
+
+ Scalar() : Poly(NK_Scalar), SKind(SK_Unknown) {}
+ Scalar(double DoubleValue)
+ : Poly(NK_Scalar), SKind(SK_Double), DoubleValue(DoubleValue) {}
+ Scalar(bool BoolValue)
+ : Poly(NK_Scalar), SKind(SK_Bool), BoolValue(BoolValue) {}
+
+ static bool classof(const Poly *N) { return N->getKind() == NK_Scalar; }
+};
+
+struct Seq : Poly, std::vector<std::unique_ptr<Poly>> {
+ Seq() : Poly(NK_Seq) {}
+
+ static bool classof(const Poly *N) { return N->getKind() == NK_Seq; }
+};
+
+struct Map : Poly, llvm::StringMap<std::unique_ptr<Poly>> {
+ Map() : Poly(NK_Map) {}
+
+ static bool classof(const Poly *N) { return N->getKind() == NK_Map; }
+};
+
+namespace llvm {
+namespace yaml {
+
+template <> struct PolymorphicTraits<std::unique_ptr<Poly>> {
+ static NodeKind getKind(const std::unique_ptr<Poly> &N) {
+ if (isa<Scalar>(*N))
+ return NodeKind::Scalar;
+ if (isa<Seq>(*N))
+ return NodeKind::Sequence;
+ if (isa<Map>(*N))
+ return NodeKind::Map;
+ llvm_unreachable("unsupported node type");
+ }
+
+ static Scalar &getAsScalar(std::unique_ptr<Poly> &N) {
+ if (!N || !isa<Scalar>(*N))
+ N = llvm::make_unique<Scalar>();
+ return *cast<Scalar>(N.get());
+ }
+
+ static Seq &getAsSequence(std::unique_ptr<Poly> &N) {
+ if (!N || !isa<Seq>(*N))
+ N = llvm::make_unique<Seq>();
+ return *cast<Seq>(N.get());
+ }
+
+ static Map &getAsMap(std::unique_ptr<Poly> &N) {
+ if (!N || !isa<Map>(*N))
+ N = llvm::make_unique<Map>();
+ return *cast<Map>(N.get());
+ }
+};
+
+template <> struct TaggedScalarTraits<Scalar> {
+ static void output(const Scalar &S, void *Ctxt, raw_ostream &ScalarOS,
+ raw_ostream &TagOS) {
+ switch (S.SKind) {
+ case Scalar::SK_Unknown:
+ report_fatal_error("output unknown scalar");
+ break;
+ case Scalar::SK_Double:
+ TagOS << "!double";
+ ScalarTraits<double>::output(S.DoubleValue, Ctxt, ScalarOS);
+ break;
+ case Scalar::SK_Bool:
+ TagOS << "!bool";
+ ScalarTraits<bool>::output(S.BoolValue, Ctxt, ScalarOS);
+ break;
+ }
+ }
+
+ static StringRef input(StringRef ScalarStr, StringRef Tag, void *Ctxt,
+ Scalar &S) {
+ S.SKind = StringSwitch<Scalar::ScalarKind>(Tag)
+ .Case("!double", Scalar::SK_Double)
+ .Case("!bool", Scalar::SK_Bool)
+ .Default(Scalar::SK_Unknown);
+ switch (S.SKind) {
+ case Scalar::SK_Unknown:
+ return StringRef("unknown scalar tag");
+ case Scalar::SK_Double:
+ return ScalarTraits<double>::input(ScalarStr, Ctxt, S.DoubleValue);
+ case Scalar::SK_Bool:
+ return ScalarTraits<bool>::input(ScalarStr, Ctxt, S.BoolValue);
+ }
+ llvm_unreachable("unknown scalar kind");
+ }
+
+ static QuotingType mustQuote(const Scalar &S, StringRef Str) {
+ switch (S.SKind) {
+ case Scalar::SK_Unknown:
+ report_fatal_error("quote unknown scalar");
+ case Scalar::SK_Double:
+ return ScalarTraits<double>::mustQuote(Str);
+ case Scalar::SK_Bool:
+ return ScalarTraits<bool>::mustQuote(Str);
+ }
+ llvm_unreachable("unknown scalar kind");
+ }
+};
+
+template <> struct CustomMappingTraits<Map> {
+ static void inputOne(IO &IO, StringRef Key, Map &M) {
+ IO.mapRequired(Key.str().c_str(), M[Key]);
+ }
+
+ static void output(IO &IO, Map &M) {
+ for (auto &N : M)
+ IO.mapRequired(N.getKey().str().c_str(), N.getValue());
+ }
+};
+
+template <> struct SequenceTraits<Seq> {
+ static size_t size(IO &IO, Seq &A) { return A.size(); }
+
+ static std::unique_ptr<Poly> &element(IO &IO, Seq &A, size_t Index) {
+ if (Index >= A.size())
+ A.resize(Index + 1);
+ return A[Index];
+ }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+TEST(YAMLIO, TestReadWritePolymorphicScalar) {
+ std::string intermediate;
+ std::unique_ptr<Poly> node = llvm::make_unique<Scalar>(true);
+
+ llvm::raw_string_ostream ostr(intermediate);
+ Output yout(ostr);
+#ifdef GTEST_HAS_DEATH_TEST
+#ifndef NDEBUG
+ EXPECT_DEATH(yout << node, "plain scalar documents are not supported");
+#endif
+#endif
+}
+
+TEST(YAMLIO, TestReadWritePolymorphicSeq) {
+ std::string intermediate;
+ {
+ auto seq = llvm::make_unique<Seq>();
+ seq->push_back(llvm::make_unique<Scalar>(true));
+ seq->push_back(llvm::make_unique<Scalar>(1.0));
+ auto node = llvm::unique_dyn_cast<Poly>(seq);
+
+ llvm::raw_string_ostream ostr(intermediate);
+ Output yout(ostr);
+ yout << node;
+ }
+ {
+ Input yin(intermediate);
+ std::unique_ptr<Poly> node;
+ yin >> node;
+
+ EXPECT_FALSE(yin.error());
+ auto seq = llvm::dyn_cast<Seq>(node.get());
+ ASSERT_TRUE(seq);
+ ASSERT_EQ(seq->size(), 2u);
+ auto first = llvm::dyn_cast<Scalar>((*seq)[0].get());
+ ASSERT_TRUE(first);
+ EXPECT_EQ(first->SKind, Scalar::SK_Bool);
+ EXPECT_TRUE(first->BoolValue);
+ auto second = llvm::dyn_cast<Scalar>((*seq)[1].get());
+ ASSERT_TRUE(second);
+ EXPECT_EQ(second->SKind, Scalar::SK_Double);
+ EXPECT_EQ(second->DoubleValue, 1.0);
+ }
+}
+
+TEST(YAMLIO, TestReadWritePolymorphicMap) {
+ std::string intermediate;
+ {
+ auto map = llvm::make_unique<Map>();
+ (*map)["foo"] = llvm::make_unique<Scalar>(false);
+ (*map)["bar"] = llvm::make_unique<Scalar>(2.0);
+ std::unique_ptr<Poly> node = llvm::unique_dyn_cast<Poly>(map);
+
+ llvm::raw_string_ostream ostr(intermediate);
+ Output yout(ostr);
+ yout << node;
+ }
+ {
+ Input yin(intermediate);
+ std::unique_ptr<Poly> node;
+ yin >> node;
+
+ EXPECT_FALSE(yin.error());
+ auto map = llvm::dyn_cast<Map>(node.get());
+ ASSERT_TRUE(map);
+ auto foo = llvm::dyn_cast<Scalar>((*map)["foo"].get());
+ ASSERT_TRUE(foo);
+ EXPECT_EQ(foo->SKind, Scalar::SK_Bool);
+ EXPECT_FALSE(foo->BoolValue);
+ auto bar = llvm::dyn_cast<Scalar>((*map)["bar"].get());
+ ASSERT_TRUE(bar);
+ EXPECT_EQ(bar->SKind, Scalar::SK_Double);
+ EXPECT_EQ(bar->DoubleValue, 2.0);
+ }
+}
OpenPOWER on IntegriCloud