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,25 @@ } }; +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> { + 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 +2003,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()); + } + +}