diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -9,6 +9,7 @@ #ifndef LLVM_SUPPORT_YAMLTRAITS_H #define LLVM_SUPPORT_YAMLTRAITS_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -1945,13 +1946,15 @@ template struct IsFlowSequenceBase {}; template <> struct IsFlowSequenceBase { static const bool flow = true; }; -template -struct SequenceTraitsImpl : IsFlowSequenceBase { -private: - using type = typename T::value_type; +template +struct IsResizable : std::false_type {}; -public: - static size_t size(IO &io, T &seq) { return seq.size(); } +template +struct IsResizable().resize(0))>> + : public std::true_type {}; + +template struct IsResizableBase { + using type = typename T::value_type; static type &element(IO &io, T &seq, size_t index) { if (index >= seq.size()) @@ -1960,6 +1963,27 @@ } }; +template struct IsResizableBase { + using type = typename T::value_type; + + static type &element(IO &io, T &seq, size_t index) { + if (index >= seq.size()) { + io.setError(Twine("value sequence extends beyond static size (") + + Twine(seq.size()) + ")"); + return seq[0]; + } + return seq[index]; + } +}; + +template +struct SequenceTraitsImpl + : IsFlowSequenceBase, IsResizableBase::value> { +private: +public: + static size_t size(IO &io, T &seq) { return seq.size(); } +}; + // Simple helper to check an expression can be used as a bool-valued template // argument. template struct CheckIsBool { static const bool value = true; }; @@ -1981,6 +2005,11 @@ SmallVectorImpl, std::enable_if_t::flow>::value>> : SequenceTraitsImpl, SequenceElementTraits::flow> {}; +template +struct SequenceTraits< + MutableArrayRef, + std::enable_if_t::flow>::value>> + : SequenceTraitsImpl, SequenceElementTraits::flow> {}; // Sequences of fundamental types use flow formatting. template diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp --- a/llvm/unittests/Support/YAMLIOTest.cpp +++ b/llvm/unittests/Support/YAMLIOTest.cpp @@ -3266,3 +3266,72 @@ yin.setCurrentDocument(); EXPECT_TRUE(yin.error()); } + +struct FixedArray { + FixedArray() { + // Initialize to int max as a sentinel value. + for (auto &V : Values) + V = std::numeric_limits::max(); + } + int Values[4]; +}; + +namespace llvm { +namespace yaml { + template <> + struct MappingTraits { + static void mapping(IO &Io, FixedArray& St) { + MutableArrayRef Array = St.Values; + Io.mapRequired("Values", Array); + } + }; +} +} + +TEST(YAMLIO, FixedSizeArray) { + FixedArray FAVal; + Input YIn("---\nValues: [ 1, 2, 3, 4 ]\n...\n"); + YIn >> FAVal; + + EXPECT_FALSE(YIn.error()); + EXPECT_EQ(FAVal.Values[0], 1); + EXPECT_EQ(FAVal.Values[1], 2); + EXPECT_EQ(FAVal.Values[2], 3); + EXPECT_EQ(FAVal.Values[3], 4); + + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + Output YOut(OS); + YOut << FAVal; + } + auto Expected = "---\n" + "Values: [ 1, 2, 3, 4 ]\n" + "...\n"; + ASSERT_EQ(Serialized, Expected); +} + +TEST(YAMLIO, FixedSizeArrayMismatch) { + { + FixedArray FAVal; + Input YIn("---\nValues: [ 1, 2, 3 ]\n...\n"); + YIn >> FAVal; + + // No error for too small, leaves the default initialized value + EXPECT_FALSE(YIn.error()); + EXPECT_EQ(FAVal.Values[0], 1); + EXPECT_EQ(FAVal.Values[1], 2); + EXPECT_EQ(FAVal.Values[2], 3); + EXPECT_EQ(FAVal.Values[3], std::numeric_limits::max()); + } + + { + FixedArray FAVal; + Input YIn("---\nValues: [ 1, 2, 3, 4, 5 ]\n...\n"); + YIn >> FAVal; + + // Error for too many elements. + EXPECT_TRUE(!!YIn.error()); + } + +}