diff --git a/include/llvm/ADT/ArrayRef.h b/include/llvm/ADT/ArrayRef.h index bfe851485f3..3efc09ddd36 100644 --- a/include/llvm/ADT/ArrayRef.h +++ b/include/llvm/ADT/ArrayRef.h @@ -195,6 +195,22 @@ namespace llvm { return slice(0, size() - N); } + /// \brief Return a copy of *this with only the first \p N elements. + LLVM_ATTRIBUTE_UNUSED_RESULT + ArrayRef take_front(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_back(size() - N); + } + + /// \brief Return a copy of *this with only the last \p N elements. + LLVM_ATTRIBUTE_UNUSED_RESULT + ArrayRef take_back(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_front(size() - N); + } + /// @} /// @name Operator Overloads /// @{ @@ -203,6 +219,22 @@ namespace llvm { return Data[Index]; } + /// Disallow accidental assignment from a temporary. + /// + /// The declaration here is extra complicated so that "arrayRef = {}" + /// continues to select the move assignment operator. + template + typename std::enable_if::value, ArrayRef>::type & + operator=(U &&Temporary) = delete; + + /// Disallow accidental assignment from a temporary. + /// + /// The declaration here is extra complicated so that "arrayRef = {}" + /// continues to select the move assignment operator. + template + typename std::enable_if::value, ArrayRef>::type & + operator=(std::initializer_list) = delete; + /// @} /// @name Expensive Operations /// @{ @@ -321,6 +353,22 @@ namespace llvm { return slice(0, this->size() - N); } + /// \brief Return a copy of *this with only the first \p N elements. + LLVM_ATTRIBUTE_UNUSED_RESULT + MutableArrayRef take_front(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_back(this->size() - N); + } + + /// \brief Return a copy of *this with only the last \p N elements. + LLVM_ATTRIBUTE_UNUSED_RESULT + MutableArrayRef take_back(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_front(this->size() - N); + } + /// @} /// @name Operator Overloads /// @{ diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 00b796f6381..6c12315ea21 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -35,7 +35,7 @@ namespace llvm { namespace detail { template -using IterOfRange = decltype(std::begin(std::declval())); +using IterOfRange = decltype(std::begin(std::declval())); } // End detail namespace @@ -369,6 +369,11 @@ struct build_index_impl<0, I...> : index_sequence {}; template struct index_sequence_for : build_index_impl {}; +/// Utility type to build an inheritance chain that makes it easy to rank +/// overload candidates. +template struct rank : rank {}; +template <> struct rank<0> {}; + //===----------------------------------------------------------------------===// // Extra additions for arrays //===----------------------------------------------------------------------===// @@ -608,6 +613,70 @@ template struct deref { } }; +namespace detail { +template class enumerator_impl { +public: + template struct result_pair { + result_pair(std::size_t Index, X Value) : Index(Index), Value(Value) {} + + const std::size_t Index; + X Value; + }; + + class iterator { + typedef + typename std::iterator_traits>::reference iter_reference; + typedef result_pair result_type; + + public: + iterator(IterOfRange &&Iter, std::size_t Index) + : Iter(Iter), Index(Index) {} + + result_type operator*() const { return result_type(Index, *Iter); } + + iterator &operator++() { + ++Iter; + ++Index; + return *this; + } + + bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; } + + private: + IterOfRange Iter; + std::size_t Index; + }; + +public: + explicit enumerator_impl(R &&Range) : Range(std::forward(Range)) {} + + iterator begin() { return iterator(std::begin(Range), 0); } + iterator end() { return iterator(std::end(Range), std::size_t(-1)); } + +private: + R Range; +}; +} + +/// Given an input range, returns a new range whose values are are pair (A,B) +/// such that A is the 0-based index of the item in the sequence, and B is +/// the value from the original sequence. Example: +/// +/// std::vector Items = {'A', 'B', 'C', 'D'}; +/// for (auto X : enumerate(Items)) { +/// printf("Item %d - %c\n", X.Item, X.Value); +/// } +/// +/// Output: +/// Item 0 - A +/// Item 1 - B +/// Item 2 - C +/// Item 3 - D +/// +template detail::enumerator_impl enumerate(R &&Range) { + return detail::enumerator_impl(std::forward(Range)); +} + } // End llvm namespace #endif diff --git a/include/llvm/ADT/StringExtras.h b/include/llvm/ADT/StringExtras.h index bdbb4d3f593..488748a5f60 100644 --- a/include/llvm/ADT/StringExtras.h +++ b/include/llvm/ADT/StringExtras.h @@ -19,6 +19,7 @@ #include namespace llvm { +class raw_ostream; template class SmallVectorImpl; /// hexdigit - Return the hexadecimal character for the @@ -150,6 +151,12 @@ static inline StringRef getOrdinalSuffix(unsigned Val) { } } +/// PrintEscapedString - Print each character of the specified string, escaping +/// it if it is not printable or if it is an escape char. +void PrintEscapedString(StringRef Name, raw_ostream &Out); + +namespace detail { + template inline std::string join_impl(IteratorT Begin, IteratorT End, StringRef Separator, std::input_iterator_tag) { @@ -184,12 +191,64 @@ inline std::string join_impl(IteratorT Begin, IteratorT End, return S; } +template +inline void join_items_impl(std::string &Result, Sep Separator) {} + +template +inline void join_items_impl(std::string &Result, Sep Separator, + const Arg &Item) { + Result += Item; +} + +template +inline void join_items_impl(std::string &Result, Sep Separator, const Arg1 &A1, + Args &&... Items) { + Result += A1; + Result += Separator; + join_items_impl(Result, Separator, std::forward(Items)...); +} + +inline size_t join_one_item_size(char C) { return 1; } +inline size_t join_one_item_size(const char *S) { return S ? ::strlen(S) : 0; } + +template inline size_t join_one_item_size(const T &Str) { + return Str.size(); +} + +inline size_t join_items_size() { return 0; } + +template inline size_t join_items_size(const A1 &A) { + return join_one_item_size(A); +} +template +inline size_t join_items_size(const A1 &A, Args &&... Items) { + return join_one_item_size(A) + join_items_size(std::forward(Items)...); +} +} + /// Joins the strings in the range [Begin, End), adding Separator between /// the elements. template inline std::string join(IteratorT Begin, IteratorT End, StringRef Separator) { typedef typename std::iterator_traits::iterator_category tag; - return join_impl(Begin, End, Separator, tag()); + return detail::join_impl(Begin, End, Separator, tag()); +} + +/// Joins the strings in the parameter pack \p Items, adding \p Separator +/// between the elements. All arguments must be implicitly convertible to +/// std::string, or there should be an overload of std::string::operator+=() +/// that accepts the argument explicitly. +template +inline std::string join_items(Sep Separator, Args &&... Items) { + std::string Result; + if (sizeof...(Items) == 0) + return Result; + + size_t NS = detail::join_one_item_size(Separator); + size_t NI = detail::join_items_size(std::forward(Items)...); + Result.reserve(NI + (sizeof...(Items) - 1) * NS + 1); + detail::join_items_impl(Result, Separator, std::forward(Items)...); + return Result; } } // End llvm namespace diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index 17a428cfd1d..a053b414a10 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -10,6 +10,7 @@ #ifndef LLVM_ADT_STRINGREF_H #define LLVM_ADT_STRINGREF_H +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Compiler.h" #include @@ -32,6 +33,10 @@ namespace llvm { bool getAsSignedInteger(StringRef Str, unsigned Radix, long long &Result); + bool consumeUnsignedInteger(StringRef &Str, unsigned Radix, + unsigned long long &Result); + bool consumeSignedInteger(StringRef &Str, unsigned Radix, long long &Result); + /// StringRef - Represent a constant reference to a string, i.e. a character /// array and a length, which need not be null terminated. /// @@ -68,12 +73,14 @@ namespace llvm { /// Construct an empty string ref. /*implicit*/ StringRef() : Data(nullptr), Length(0) {} + /// Disable conversion from nullptr. This prevents things like + /// if (S == nullptr) + StringRef(std::nullptr_t) = delete; + /// Construct a string ref from a cstring. + LLVM_ATTRIBUTE_ALWAYS_INLINE /*implicit*/ StringRef(const char *Str) - : Data(Str) { - assert(Str && "StringRef cannot be built from a NULL argument"); - Length = ::strlen(Str); // invoking strlen(NULL) is undefined behavior - } + : Data(Str), Length(Str ? ::strlen(Str) : 0) {} /// Construct a string ref from a pointer and length. LLVM_ATTRIBUTE_ALWAYS_INLINE @@ -88,6 +95,10 @@ namespace llvm { /*implicit*/ StringRef(const std::string &Str) : Data(Str.data()), Length(Str.length()) {} + static StringRef withNullAsEmpty(const char *data) { + return StringRef(data ? data : ""); + } + /// @} /// @name Iterators /// @{ @@ -266,6 +277,32 @@ namespace llvm { return npos; } + /// Search for the first character satisfying the predicate \p F + /// + /// \returns The index of the first character satisfying \p F starting from + /// \p From, or npos if not found. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + size_t find_if(function_ref F, size_t From = 0) const { + StringRef S = drop_front(From); + while (!S.empty()) { + if (F(S.front())) + return size() - S.size(); + S = S.drop_front(); + } + return npos; + } + + /// Search for the first character not satisfying the predicate \p F + /// + /// \returns The index of the first character not satisfying \p F starting + /// from \p From, or npos if not found. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + size_t find_if_not(function_ref F, size_t From = 0) const { + return find_if([F](char c) { return !F(c); }, From); + } + /// Search for the first string \p Str in the string. /// /// \returns The index of the first occurrence of \p Str, or npos if not @@ -337,6 +374,16 @@ namespace llvm { /// Complexity: O(size() + Chars.size()) size_t find_last_not_of(StringRef Chars, size_t From = npos) const; + /// Return true if the given string is a substring of *this, and false + /// otherwise. + LLVM_ATTRIBUTE_ALWAYS_INLINE + bool contains(StringRef Other) const { return find(Other) != npos; } + + /// Return true if the given character is contained in *this, and false + /// otherwise. + LLVM_ATTRIBUTE_ALWAYS_INLINE + bool contains(char C) const { return find_first_of(C) != npos; } + /// @} /// @name Helpful Algorithms /// @{ @@ -386,6 +433,37 @@ namespace llvm { return false; } + /// Parse the current string as an integer of the specified radix. If + /// \p Radix is specified as zero, this does radix autosensing using + /// extended C rules: 0 is octal, 0x is hex, 0b is binary. + /// + /// If the string does not begin with a number of the specified radix, + /// this returns true to signify the error. The string is considered + /// erroneous if empty or if it overflows T. + /// The portion of the string representing the discovered numeric value + /// is removed from the beginning of the string. + template + typename std::enable_if::is_signed, bool>::type + consumeInteger(unsigned Radix, T &Result) { + long long LLVal; + if (consumeSignedInteger(*this, Radix, LLVal) || + static_cast(static_cast(LLVal)) != LLVal) + return true; + Result = LLVal; + return false; + } + + template + typename std::enable_if::is_signed, bool>::type + consumeInteger(unsigned Radix, T &Result) { + unsigned long long ULLVal; + if (consumeUnsignedInteger(*this, Radix, ULLVal) || + static_cast(static_cast(ULLVal)) != ULLVal) + return true; + Result = ULLVal; + return false; + } + /// Parse the current string as an integer of the specified \p Radix, or of /// an autosensed radix if the \p Radix given is 0. The current value in /// \p Result is discarded, and the storage is changed to be wide enough to @@ -428,6 +506,44 @@ namespace llvm { return StringRef(Data + Start, std::min(N, Length - Start)); } + /// Return a StringRef equal to 'this' but with only the first \p N + /// elements remaining. If \p N is greater than the length of the + /// string, the entire string is returned. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef take_front(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_back(size() - N); + } + + /// Return a StringRef equal to 'this' but with only the first \p N + /// elements remaining. If \p N is greater than the length of the + /// string, the entire string is returned. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef take_back(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_front(size() - N); + } + + /// Return the longest prefix of 'this' such that every character + /// in the prefix satisfies the given predicate. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef take_while(function_ref F) const { + return substr(0, find_if_not(F)); + } + + /// Return the longest prefix of 'this' such that no character in + /// the prefix satisfies the given predicate. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef take_until(function_ref F) const { + return substr(0, find_if(F)); + } + /// Return a StringRef equal to 'this' but with the first \p N elements /// dropped. LLVM_ATTRIBUTE_ALWAYS_INLINE @@ -446,6 +562,22 @@ namespace llvm { return substr(0, size()-N); } + /// Return a StringRef equal to 'this', but with all characters satisfying + /// the given predicate dropped from the beginning of the string. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef drop_while(function_ref F) const { + return substr(find_if_not(F)); + } + + /// Return a StringRef equal to 'this', but with all characters not + /// satisfying the given predicate dropped from the beginning of the string. + LLVM_ATTRIBUTE_ALWAYS_INLINE + LLVM_ATTRIBUTE_UNUSED_RESULT + StringRef drop_until(function_ref F) const { + return substr(find_if(F)); + } + /// Returns true if this StringRef has the given prefix and removes that /// prefix. LLVM_ATTRIBUTE_ALWAYS_INLINE diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index 552851da3aa..bf00ee291a2 100644 --- a/lib/IR/AsmWriter.cpp +++ b/lib/IR/AsmWriter.cpp @@ -336,9 +336,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { } } -// PrintEscapedString - Print each character of the specified string, escaping -// it if it is not printable or if it is an escape char. -static void PrintEscapedString(StringRef Name, raw_ostream &Out) { +void llvm::PrintEscapedString(StringRef Name, raw_ostream &Out) { for (unsigned i = 0, e = Name.size(); i != e; ++i) { unsigned char C = Name[i]; if (isprint(C) && C != '\\' && C != '"') diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index 93b3d2885be..dedafb5446e 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -381,10 +381,18 @@ std::string Attribute::getAsString(bool InAttrGrp) const { std::string Result; Result += (Twine('"') + getKindAsString() + Twine('"')).str(); - StringRef Val = pImpl->getValueAsString(); - if (Val.empty()) return Result; - - Result += ("=\"" + Val + Twine('"')).str(); + std::string AttrVal = pImpl->getValueAsString(); + if (AttrVal.empty()) return Result; + + // Since some attribute strings contain special characters that cannot be + // printable, those have to be escaped to make the attribute value printable + // as is. e.g. "\01__gnu_mcount_nc" + { + raw_string_ostream OS(Result); + OS << "=\""; + PrintEscapedString(AttrVal, OS); + OS << "\""; + } return Result; } diff --git a/lib/Support/StringRef.cpp b/lib/Support/StringRef.cpp index 8a9da5edca8..51e0394d8bd 100644 --- a/lib/Support/StringRef.cpp +++ b/lib/Support/StringRef.cpp @@ -351,6 +351,9 @@ size_t StringRef::count(StringRef Str) const { } static unsigned GetAutoSenseRadix(StringRef &Str) { + if (Str.empty()) + return 10; + if (Str.startswith("0x") || Str.startswith("0X")) { Str = Str.substr(2); return 16; @@ -366,17 +369,16 @@ static unsigned GetAutoSenseRadix(StringRef &Str) { return 8; } - if (Str.startswith("0")) + if (Str[0] == '0' && Str.size() > 1 && ascii_isdigit(Str[1])) { + Str = Str.substr(1); return 8; - + } + return 10; } - -/// GetAsUnsignedInteger - Workhorse method that converts a integer character -/// sequence of radix up to 36 to an unsigned long long value. -bool llvm::getAsUnsignedInteger(StringRef Str, unsigned Radix, - unsigned long long &Result) { +bool llvm::consumeUnsignedInteger(StringRef &Str, unsigned Radix, + unsigned long long &Result) { // Autosense radix if not specified. if (Radix == 0) Radix = GetAutoSenseRadix(Str); @@ -385,44 +387,51 @@ bool llvm::getAsUnsignedInteger(StringRef Str, unsigned Radix, if (Str.empty()) return true; // Parse all the bytes of the string given this radix. Watch for overflow. + StringRef Str2 = Str; Result = 0; - while (!Str.empty()) { + while (!Str2.empty()) { unsigned CharVal; - if (Str[0] >= '0' && Str[0] <= '9') - CharVal = Str[0]-'0'; - else if (Str[0] >= 'a' && Str[0] <= 'z') - CharVal = Str[0]-'a'+10; - else if (Str[0] >= 'A' && Str[0] <= 'Z') - CharVal = Str[0]-'A'+10; + if (Str2[0] >= '0' && Str2[0] <= '9') + CharVal = Str2[0] - '0'; + else if (Str2[0] >= 'a' && Str2[0] <= 'z') + CharVal = Str2[0] - 'a' + 10; + else if (Str2[0] >= 'A' && Str2[0] <= 'Z') + CharVal = Str2[0] - 'A' + 10; else - return true; + break; - // If the parsed value is larger than the integer radix, the string is - // invalid. + // If the parsed value is larger than the integer radix, we cannot + // consume any more characters. if (CharVal >= Radix) - return true; + break; // Add in this character. unsigned long long PrevResult = Result; - Result = Result*Radix+CharVal; + Result = Result * Radix + CharVal; // Check for overflow by shifting back and seeing if bits were lost. - if (Result/Radix < PrevResult) + if (Result / Radix < PrevResult) return true; - Str = Str.substr(1); + Str2 = Str2.substr(1); } + // We consider the operation a failure if no characters were consumed + // successfully. + if (Str.size() == Str2.size()) + return true; + + Str = Str2; return false; } -bool llvm::getAsSignedInteger(StringRef Str, unsigned Radix, - long long &Result) { +bool llvm::consumeSignedInteger(StringRef &Str, unsigned Radix, + long long &Result) { unsigned long long ULLVal; // Handle positive strings first. if (Str.empty() || Str.front() != '-') { - if (getAsUnsignedInteger(Str, Radix, ULLVal) || + if (consumeUnsignedInteger(Str, Radix, ULLVal) || // Check for value so large it overflows a signed value. (long long)ULLVal < 0) return true; @@ -431,17 +440,41 @@ bool llvm::getAsSignedInteger(StringRef Str, unsigned Radix, } // Get the positive part of the value. - if (getAsUnsignedInteger(Str.substr(1), Radix, ULLVal) || + StringRef Str2 = Str.drop_front(1); + if (consumeUnsignedInteger(Str2, Radix, ULLVal) || // Reject values so large they'd overflow as negative signed, but allow // "-0". This negates the unsigned so that the negative isn't undefined // on signed overflow. (long long)-ULLVal > 0) return true; + Str = Str2; Result = -ULLVal; return false; } +/// GetAsUnsignedInteger - Workhorse method that converts a integer character +/// sequence of radix up to 36 to an unsigned long long value. +bool llvm::getAsUnsignedInteger(StringRef Str, unsigned Radix, + unsigned long long &Result) { + if (consumeUnsignedInteger(Str, Radix, Result)) + return true; + + // For getAsUnsignedInteger, we require the whole string to be consumed or + // else we consider it a failure. + return !Str.empty(); +} + +bool llvm::getAsSignedInteger(StringRef Str, unsigned Radix, + long long &Result) { + if (consumeSignedInteger(Str, Radix, Result)) + return true; + + // For getAsSignedInteger, we require the whole string to be consumed or else + // we consider it a failure. + return !Str.empty(); +} + bool StringRef::getAsInteger(unsigned Radix, APInt &Result) const { StringRef Str = *this; diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp index b5b71f06f65..a6a75a170e0 100644 --- a/unittests/ADT/ArrayRefTest.cpp +++ b/unittests/ADT/ArrayRefTest.cpp @@ -31,6 +31,26 @@ static_assert( !std::is_convertible, ArrayRef>::value, "Removing volatile"); +// Check that we can't accidentally assign a temporary location to an ArrayRef. +// (Unfortunately we can't make use of the same thing with constructors.) +// +// Disable this check under MSVC; even MSVC 2015 isn't inconsistent between +// std::is_assignable and actually writing such an assignment. +#if !defined(_MSC_VER) +static_assert( + !std::is_assignable, int *>::value, + "Assigning from single prvalue element"); +static_assert( + !std::is_assignable, int * &&>::value, + "Assigning from single xvalue element"); +static_assert( + std::is_assignable, int * &>::value, + "Assigning from single lvalue element"); +static_assert( + !std::is_assignable, std::initializer_list>::value, + "Assigning from an initializer list"); +#endif + namespace { TEST(ArrayRefTest, AllocatorCopy) { @@ -82,6 +102,20 @@ TEST(ArrayRefTest, DropFront) { EXPECT_EQ(1U, AR3.drop_front(AR3.size() - 1).size()); } +TEST(ArrayRefTest, KeepBack) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(AR1.end() - 1, 1); + EXPECT_TRUE(AR1.keep_back().equals(AR2)); +} + +TEST(ArrayRefTest, KeepFront) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(AR1.data(), 2); + EXPECT_TRUE(AR1.keep_front(2).equals(AR2)); +} + TEST(ArrayRefTest, Equals) { static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; ArrayRef AR1(A1); @@ -146,6 +180,14 @@ TEST(ArrayRefTest, InitializerList) { ArgTest12({1, 2}); } +TEST(ArrayRefTest, EmptyInitializerList) { + ArrayRef A = {}; + EXPECT_TRUE(A.empty()); + + A = {}; + EXPECT_TRUE(A.empty()); +} + // Test that makeArrayRef works on ArrayRef (no-op) TEST(ArrayRefTest, makeArrayRef) { static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 433209876f1..c33ce4121c7 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -35,6 +35,7 @@ set(ADTSources PriorityWorklistTest.cpp RangeAdapterTest.cpp SCCIteratorTest.cpp + STLExtrasTest.cpp ScopeExitTest.cpp SequenceTest.cpp SetVectorTest.cpp @@ -44,6 +45,7 @@ set(ADTSources SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StringExtrasTest.cpp StringMapTest.cpp StringRefTest.cpp TinyPtrVectorTest.cpp diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp new file mode 100644 index 00000000000..db344bc6089 --- /dev/null +++ b/unittests/ADT/STLExtrasTest.cpp @@ -0,0 +1,195 @@ +//===- STLExtrasTest.cpp - Unit tests for STL extras ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "gtest/gtest.h" + +#include + +using namespace llvm; + +namespace { + +int f(rank<0>) { return 0; } +int f(rank<1>) { return 1; } +int f(rank<2>) { return 2; } +int f(rank<4>) { return 4; } + +TEST(STLExtrasTest, Rank) { + // We shouldn't get ambiguities and should select the overload of the same + // rank as the argument. + EXPECT_EQ(0, f(rank<0>())); + EXPECT_EQ(1, f(rank<1>())); + EXPECT_EQ(2, f(rank<2>())); + + // This overload is missing so we end up back at 2. + EXPECT_EQ(2, f(rank<3>())); + + // But going past 3 should work fine. + EXPECT_EQ(4, f(rank<4>())); + + // And we can even go higher and just fall back to the last overload. + EXPECT_EQ(4, f(rank<5>())); + EXPECT_EQ(4, f(rank<6>())); +} + +TEST(STLExtrasTest, EnumerateLValue) { + // Test that a simple LValue can be enumerated and gives correct results with + // multiple types, including the empty container. + std::vector foo = {'a', 'b', 'c'}; + typedef std::pair CharPairType; + std::vector CharResults; + + for (auto X : llvm::enumerate(foo)) { + CharResults.emplace_back(X.Index, X.Value); + } + ASSERT_EQ(3u, CharResults.size()); + EXPECT_EQ(CharPairType(0u, 'a'), CharResults[0]); + EXPECT_EQ(CharPairType(1u, 'b'), CharResults[1]); + EXPECT_EQ(CharPairType(2u, 'c'), CharResults[2]); + + // Test a const range of a different type. + typedef std::pair IntPairType; + std::vector IntResults; + const std::vector bar = {1, 2, 3}; + for (auto X : llvm::enumerate(bar)) { + IntResults.emplace_back(X.Index, X.Value); + } + ASSERT_EQ(3u, IntResults.size()); + EXPECT_EQ(IntPairType(0u, 1), IntResults[0]); + EXPECT_EQ(IntPairType(1u, 2), IntResults[1]); + EXPECT_EQ(IntPairType(2u, 3), IntResults[2]); + + // Test an empty range. + IntResults.clear(); + const std::vector baz; + for (auto X : llvm::enumerate(baz)) { + IntResults.emplace_back(X.Index, X.Value); + } + EXPECT_TRUE(IntResults.empty()); +} + +TEST(STLExtrasTest, EnumerateModifyLValue) { + // Test that you can modify the underlying entries of an lvalue range through + // the enumeration iterator. + std::vector foo = {'a', 'b', 'c'}; + + for (auto X : llvm::enumerate(foo)) { + ++X.Value; + } + EXPECT_EQ('b', foo[0]); + EXPECT_EQ('c', foo[1]); + EXPECT_EQ('d', foo[2]); +} + +TEST(STLExtrasTest, EnumerateRValueRef) { + // Test that an rvalue can be enumerated. + typedef std::pair PairType; + std::vector Results; + + auto Enumerator = llvm::enumerate(std::vector{1, 2, 3}); + + for (auto X : llvm::enumerate(std::vector{1, 2, 3})) { + Results.emplace_back(X.Index, X.Value); + } + + ASSERT_EQ(3u, Results.size()); + EXPECT_EQ(PairType(0u, 1), Results[0]); + EXPECT_EQ(PairType(1u, 2), Results[1]); + EXPECT_EQ(PairType(2u, 3), Results[2]); +} + +TEST(STLExtrasTest, EnumerateModifyRValue) { + // Test that when enumerating an rvalue, modification still works (even if + // this isn't terribly useful, it at least shows that we haven't snuck an + // extra const in there somewhere. + typedef std::pair PairType; + std::vector Results; + + for (auto X : llvm::enumerate(std::vector{'1', '2', '3'})) { + ++X.Value; + Results.emplace_back(X.Index, X.Value); + } + + ASSERT_EQ(3u, Results.size()); + EXPECT_EQ(PairType(0u, '2'), Results[0]); + EXPECT_EQ(PairType(1u, '3'), Results[1]); + EXPECT_EQ(PairType(2u, '4'), Results[2]); +} + +template struct CanMove {}; +template <> struct CanMove { + CanMove(CanMove &&) = delete; + + CanMove() = default; + CanMove(const CanMove &) = default; +}; + +template struct CanCopy {}; +template <> struct CanCopy { + CanCopy(const CanCopy &) = delete; + + CanCopy() = default; + // FIXME: Use '= default' when we drop MSVC 2013. + CanCopy(CanCopy &&) {} +}; + +template +struct Range : CanMove, CanCopy { + explicit Range(int &C, int &M, int &D) : C(C), M(M), D(D) {} + Range(const Range &R) : CanCopy(R), C(R.C), M(R.M), D(R.D) { ++C; } + Range(Range &&R) : CanMove(std::move(R)), C(R.C), M(R.M), D(R.D) { + ++M; + } + ~Range() { ++D; } + + int &C; + int &M; + int &D; + + int *begin() { return nullptr; } + int *end() { return nullptr; } +}; + +TEST(STLExtrasTest, EnumerateLifetimeSemantics) { + // Test that when enumerating lvalues and rvalues, there are no surprise + // copies or moves. + + // With an rvalue, it should not be destroyed until the end of the scope. + int Copies = 0; + int Moves = 0; + int Destructors = 0; + { + auto E1 = enumerate(Range(Copies, Moves, Destructors)); + // Doesn't compile. rvalue ranges must be moveable. + // auto E2 = enumerate(Range(Copies, Moves, Destructors)); + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(1, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(1, Moves); + EXPECT_EQ(2, Destructors); + + Copies = Moves = Destructors = 0; + // With an lvalue, it should not be destroyed even after the end of the scope. + // lvalue ranges need be neither copyable nor moveable. + Range R(Copies, Moves, Destructors); + { + auto Enumerator = enumerate(R); + (void)Enumerator; + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); + } + EXPECT_EQ(0, Copies); + EXPECT_EQ(0, Moves); + EXPECT_EQ(0, Destructors); +} +} diff --git a/unittests/ADT/StringExtrasTest.cpp b/unittests/ADT/StringExtrasTest.cpp new file mode 100644 index 00000000000..afb984e405d --- /dev/null +++ b/unittests/ADT/StringExtrasTest.cpp @@ -0,0 +1,52 @@ +//===- StringExtrasTest.cpp - Unit tests for String extras ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(StringExtrasTest, Join) { + std::vector Items; + EXPECT_EQ("", join(Items.begin(), Items.end(), " ")); + + Items = {"foo"}; + EXPECT_EQ("foo", join(Items.begin(), Items.end(), " ")); + + Items = {"foo", "bar"}; + EXPECT_EQ("foo bar", join(Items.begin(), Items.end(), " ")); + + Items = {"foo", "bar", "baz"}; + EXPECT_EQ("foo bar baz", + join(Items.begin(), Items.end(), " ")); +} + +TEST(StringExtrasTest, JoinItems) { + const char *Foo = "foo"; + std::string Bar = "bar"; + llvm::StringRef Baz = "baz"; + char X = 'x'; + + EXPECT_EQ("", join_items(" ")); + EXPECT_EQ("", join_items('/')); + + EXPECT_EQ("foo", join_items(" ", Foo)); + EXPECT_EQ("foo", join_items('/', Foo)); + + EXPECT_EQ("foo bar", join_items(" ", Foo, Bar)); + EXPECT_EQ("foo/bar", join_items('/', Foo, Bar)); + + EXPECT_EQ("foo bar baz", join_items(" ", Foo, Bar, Baz)); + EXPECT_EQ("foo/bar/baz", join_items('/', Foo, Bar, Baz)); + + EXPECT_EQ("foo bar baz x", + join_items(" ", Foo, Bar, Baz, X)); + + EXPECT_EQ("foo/bar/baz/x", join_items('/', Foo, Bar, Baz, X)); +} diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 72018869742..ca6b011d695 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -571,7 +571,8 @@ TEST(StringRefTest, getAsInteger) { static const char* BadStrings[] = { - "18446744073709551617" // value just over max + "" // empty string + , "18446744073709551617" // value just over max , "123456789012345678901" // value way too large , "4t23v" // illegal decimal characters , "0x123W56" // illegal hex characters @@ -579,6 +580,8 @@ static const char* BadStrings[] = { , "08" // illegal oct characters , "0o8" // illegal oct characters , "-123" // negative unsigned value + , "0x" + , "0b" }; @@ -590,6 +593,183 @@ TEST(StringRefTest, getAsUnsignedIntegerBadStrings) { } } +struct ConsumeUnsignedPair { + const char *Str; + uint64_t Expected; + const char *Leftover; +} ConsumeUnsigned[] = { + {"0", 0, ""}, + {"255", 255, ""}, + {"256", 256, ""}, + {"65535", 65535, ""}, + {"65536", 65536, ""}, + {"4294967295", 4294967295ULL, ""}, + {"4294967296", 4294967296ULL, ""}, + {"255A376", 255, "A376"}, + {"18446744073709551615", 18446744073709551615ULL, ""}, + {"18446744073709551615ABC", 18446744073709551615ULL, "ABC"}, + {"042", 34, ""}, + {"0x42", 66, ""}, + {"0x42-0x34", 66, "-0x34"}, + {"0b101010", 42, ""}, + {"0429F", 042, "9F"}, // Auto-sensed octal radix, invalid digit + {"0x42G12", 0x42, "G12"}, // Auto-sensed hex radix, invalid digit + {"0b10101020101", 42, "20101"}}; // Auto-sensed binary radix, invalid digit. + +struct ConsumeSignedPair { + const char *Str; + int64_t Expected; + const char *Leftover; +} ConsumeSigned[] = { + {"0", 0, ""}, + {"-0", 0, ""}, + {"0-1", 0, "-1"}, + {"-0-1", 0, "-1"}, + {"127", 127, ""}, + {"128", 128, ""}, + {"127-1", 127, "-1"}, + {"128-1", 128, "-1"}, + {"-128", -128, ""}, + {"-129", -129, ""}, + {"-128-1", -128, "-1"}, + {"-129-1", -129, "-1"}, + {"32767", 32767, ""}, + {"32768", 32768, ""}, + {"32767-1", 32767, "-1"}, + {"32768-1", 32768, "-1"}, + {"-32768", -32768, ""}, + {"-32769", -32769, ""}, + {"-32768-1", -32768, "-1"}, + {"-32769-1", -32769, "-1"}, + {"2147483647", 2147483647LL, ""}, + {"2147483648", 2147483648LL, ""}, + {"2147483647-1", 2147483647LL, "-1"}, + {"2147483648-1", 2147483648LL, "-1"}, + {"-2147483648", -2147483648LL, ""}, + {"-2147483649", -2147483649LL, ""}, + {"-2147483648-1", -2147483648LL, "-1"}, + {"-2147483649-1", -2147483649LL, "-1"}, + {"-9223372036854775808", -(9223372036854775807LL) - 1, ""}, + {"-9223372036854775808-1", -(9223372036854775807LL) - 1, "-1"}, + {"042", 34, ""}, + {"042-1", 34, "-1"}, + {"0x42", 66, ""}, + {"0x42-1", 66, "-1"}, + {"0b101010", 42, ""}, + {"0b101010-1", 42, "-1"}, + {"-042", -34, ""}, + {"-042-1", -34, "-1"}, + {"-0x42", -66, ""}, + {"-0x42-1", -66, "-1"}, + {"-0b101010", -42, ""}, + {"-0b101010-1", -42, "-1"}}; + +TEST(StringRefTest, consumeIntegerUnsigned) { + uint8_t U8; + uint16_t U16; + uint32_t U32; + uint64_t U64; + + for (size_t i = 0; i < array_lengthof(ConsumeUnsigned); ++i) { + StringRef Str = ConsumeUnsigned[i].Str; + bool U8Success = Str.consumeInteger(0, U8); + if (static_cast(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U8Success); + EXPECT_EQ(U8, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U8Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U16Success = Str.consumeInteger(0, U16); + if (static_cast(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U16Success); + EXPECT_EQ(U16, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U16Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U32Success = Str.consumeInteger(0, U32); + if (static_cast(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U32Success); + EXPECT_EQ(U32, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U32Success); + } + + Str = ConsumeUnsigned[i].Str; + bool U64Success = Str.consumeInteger(0, U64); + if (static_cast(ConsumeUnsigned[i].Expected) == + ConsumeUnsigned[i].Expected) { + ASSERT_FALSE(U64Success); + EXPECT_EQ(U64, ConsumeUnsigned[i].Expected); + EXPECT_EQ(Str, ConsumeUnsigned[i].Leftover); + } else { + ASSERT_TRUE(U64Success); + } + } +} + +TEST(StringRefTest, consumeIntegerSigned) { + int8_t S8; + int16_t S16; + int32_t S32; + int64_t S64; + + for (size_t i = 0; i < array_lengthof(ConsumeSigned); ++i) { + StringRef Str = ConsumeSigned[i].Str; + bool S8Success = Str.consumeInteger(0, S8); + if (static_cast(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S8Success); + EXPECT_EQ(S8, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S8Success); + } + + Str = ConsumeSigned[i].Str; + bool S16Success = Str.consumeInteger(0, S16); + if (static_cast(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S16Success); + EXPECT_EQ(S16, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S16Success); + } + + Str = ConsumeSigned[i].Str; + bool S32Success = Str.consumeInteger(0, S32); + if (static_cast(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S32Success); + EXPECT_EQ(S32, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S32Success); + } + + Str = ConsumeSigned[i].Str; + bool S64Success = Str.consumeInteger(0, S64); + if (static_cast(ConsumeSigned[i].Expected) == + ConsumeSigned[i].Expected) { + ASSERT_FALSE(S64Success); + EXPECT_EQ(S64, ConsumeSigned[i].Expected); + EXPECT_EQ(Str, ConsumeSigned[i].Leftover); + } else { + ASSERT_TRUE(S64Success); + } + } +} + static const char *join_input[] = { "a", "b", "c" }; static const char join_result1[] = "a"; static const char join_result2[] = "a:b:c"; @@ -640,5 +820,104 @@ TEST(StringRefTest, AllocatorCopy) { EXPECT_NE(Str2.data(), Str2c.data()); } +TEST(StringRefTest, Drop) { + StringRef Test("StringRefTest::Drop"); + + StringRef Dropped = Test.drop_front(5); + EXPECT_EQ(Dropped, "gRefTest::Drop"); + + Dropped = Test.drop_back(5); + EXPECT_EQ(Dropped, "StringRefTest:"); + + Dropped = Test.drop_front(0); + EXPECT_EQ(Dropped, Test); + + Dropped = Test.drop_back(0); + EXPECT_EQ(Dropped, Test); + + Dropped = Test.drop_front(Test.size()); + EXPECT_TRUE(Dropped.empty()); + + Dropped = Test.drop_back(Test.size()); + EXPECT_TRUE(Dropped.empty()); +} + +TEST(StringRefTest, Take) { + StringRef Test("StringRefTest::Take"); + + StringRef Taken = Test.take_front(5); + EXPECT_EQ(Taken, "Strin"); + + Taken = Test.take_back(5); + EXPECT_EQ(Taken, ":Take"); + + Taken = Test.take_front(Test.size()); + EXPECT_EQ(Taken, Test); + + Taken = Test.take_back(Test.size()); + EXPECT_EQ(Taken, Test); + + Taken = Test.take_front(0); + EXPECT_TRUE(Taken.empty()); + + Taken = Test.take_back(0); + EXPECT_TRUE(Taken.empty()); +} + +TEST(StringRefTest, FindIf) { + StringRef Punct("Test.String"); + StringRef NoPunct("ABCDEFG"); + StringRef Empty; + + auto IsPunct = [](char c) { return ::ispunct(c); }; + auto IsAlpha = [](char c) { return ::isalpha(c); }; + EXPECT_EQ(4U, Punct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct)); + + EXPECT_EQ(4U, Punct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if_not(IsAlpha)); + EXPECT_EQ(StringRef::npos, Empty.find_if_not(IsAlpha)); +} + +TEST(StringRefTest, TakeWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.take_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ("", Taken); + + Taken = Test.take_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("String With ", Taken); + + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + Taken = Test.take_until([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Test = ""; + Taken = Test.take_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} + +TEST(StringRefTest, DropWhileUntil) { + StringRef Test("String With 1 Number"); + + StringRef Taken = Test.drop_while([](char c) { return ::isdigit(c); }); + EXPECT_EQ(Test, Taken); + + Taken = Test.drop_until([](char c) { return ::isdigit(c); }); + EXPECT_EQ("1 Number", Taken); + + Taken = Test.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); + + Taken = Test.drop_until([](char c) { return true; }); + EXPECT_EQ(Test, Taken); + + StringRef EmptyString = ""; + Taken = EmptyString.drop_while([](char c) { return true; }); + EXPECT_EQ("", Taken); +} } // end anonymous namespace