From 30d8981e7c179f71c0e477958472e827b94fbfdd Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Fri, 19 Aug 2016 02:07:51 +0000 Subject: [PATCH 01/23] [ADT] Add the worlds simplest STL extra. Or at least close to it. This is a little class template that just builds an inheritance chain of empty classes. Despite how simple this is, it can be used to really nicely create ranked overload sets. I've added a unittest as much to document this as test it. You can pass an object of this type as an argument to a function overload set an it will call the first viable and enabled candidate at or below the rank of the object. I'm planning to use this in a subsequent commit to more clearly rank overload candidates used for SFINAE. All credit for this technique and both lines of code here to Richard Smith who was helping me rewrite the SFINAE check in question to much more effectively capture the intended set of checks. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@279197 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d319716acc5e19c7139fa86dfa4f67275e9c4e2d) --- include/llvm/ADT/STLExtras.h | 5 +++++ unittests/ADT/CMakeLists.txt | 1 + unittests/ADT/STLExtrasTest.cpp | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 unittests/ADT/STLExtrasTest.cpp diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 00b796f6381..821f1cc24d1 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -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 //===----------------------------------------------------------------------===// diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 433209876f1..a4c68fb2aab 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 diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp new file mode 100644 index 00000000000..dc62b03741c --- /dev/null +++ b/unittests/ADT/STLExtrasTest.cpp @@ -0,0 +1,40 @@ +//===- 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" + +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>())); +} + +} From 16c9aae12662786a004af372cd6721b4ef580ccc Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Tue, 30 Aug 2016 17:29:59 +0000 Subject: [PATCH 02/23] Add StringRef::take_front and StringRef::take_back Reviewed By: majnemer, rnk Differential Revision: https://reviews.llvm.org/D23965 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280114 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit bb0403b31b0969c26bdd2f86b1ede66aad72d020) --- include/llvm/ADT/ArrayRef.h | 32 ++++++++++++++++++++++++ include/llvm/ADT/StringRef.h | 22 +++++++++++++++++ unittests/ADT/ArrayRefTest.cpp | 14 +++++++++++ unittests/ADT/StringRefTest.cpp | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/include/llvm/ADT/ArrayRef.h b/include/llvm/ADT/ArrayRef.h index bfe851485f3..6a6d581eb29 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 Keep the first \p N elements of the array. + LLVM_ATTRIBUTE_UNUSED_RESULT + ArrayRef keep_front(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_back(size() - N); + } + + /// \brief Keep the last \p N elements of the array. + LLVM_ATTRIBUTE_UNUSED_RESULT + ArrayRef keep_back(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_front(size() - N); + } + /// @} /// @name Operator Overloads /// @{ @@ -321,6 +337,22 @@ namespace llvm { return slice(0, this->size() - N); } + /// \brief Drop everything but the first \p N elements of the array. + LLVM_ATTRIBUTE_UNUSED_RESULT + MutableArrayRef keep_front(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_back(size() - N); + } + + /// \brief Drop everything but the last \p N elements of the array. + LLVM_ATTRIBUTE_UNUSED_RESULT + MutableArrayRef keep_back(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_front(size() - N); + } + /// @} /// @name Operator Overloads /// @{ diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index 17a428cfd1d..f18cf020416 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -428,6 +428,28 @@ 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 a StringRef equal to 'this' but with the first \p N elements /// dropped. LLVM_ATTRIBUTE_ALWAYS_INLINE diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp index b5b71f06f65..840d53f786b 100644 --- a/unittests/ADT/ArrayRefTest.cpp +++ b/unittests/ADT/ArrayRefTest.cpp @@ -82,6 +82,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); diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 72018869742..97ddaf6adcc 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -640,5 +640,48 @@ 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()); +} } // end anonymous namespace From 58381611f418099fca810e5557868a359d4476cb Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Tue, 30 Aug 2016 17:38:28 +0000 Subject: [PATCH 03/23] Appease buildbots after r280114. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280117 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b8d741f27e57b10e7c9c4c80ecb56f2c6810f66a) --- include/llvm/ADT/ArrayRef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/llvm/ADT/ArrayRef.h b/include/llvm/ADT/ArrayRef.h index 6a6d581eb29..47e1398b5df 100644 --- a/include/llvm/ADT/ArrayRef.h +++ b/include/llvm/ADT/ArrayRef.h @@ -342,7 +342,7 @@ namespace llvm { MutableArrayRef keep_front(size_t N = 1) const { if (N >= this->size()) return *this; - return drop_back(size() - N); + return drop_back(this->size() - N); } /// \brief Drop everything but the last \p N elements of the array. @@ -350,7 +350,7 @@ namespace llvm { MutableArrayRef keep_back(size_t N = 1) const { if (N >= this->size()) return *this; - return drop_front(size() - N); + return drop_front(this->size() - N); } /// @} From 9956a48d44a03bc0f7aa1546ba3e2006b695e79a Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Tue, 30 Aug 2016 18:19:18 +0000 Subject: [PATCH 04/23] Rename ArrayRef::keep_front / keep_back to take_front / take_back. The name decided on was take_, but I only updated it for StringRef and forgot to do it for ArrayRef. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280126 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b986a763970f7d018972c2774c3aa26a5fb8fe12) --- include/llvm/ADT/ArrayRef.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/llvm/ADT/ArrayRef.h b/include/llvm/ADT/ArrayRef.h index 47e1398b5df..c1d66c69903 100644 --- a/include/llvm/ADT/ArrayRef.h +++ b/include/llvm/ADT/ArrayRef.h @@ -195,17 +195,17 @@ namespace llvm { return slice(0, size() - N); } - /// \brief Keep the first \p N elements of the array. + /// \brief Return a copy of *this with only the first \p N elements. LLVM_ATTRIBUTE_UNUSED_RESULT - ArrayRef keep_front(size_t N = 1) const { + ArrayRef take_front(size_t N = 1) const { if (N >= size()) return *this; return drop_back(size() - N); } - /// \brief Keep the last \p N elements of the array. + /// \brief Return a copy of *this with only the last \p N elements. LLVM_ATTRIBUTE_UNUSED_RESULT - ArrayRef keep_back(size_t N = 1) const { + ArrayRef take_back(size_t N = 1) const { if (N >= size()) return *this; return drop_front(size() - N); @@ -337,17 +337,17 @@ namespace llvm { return slice(0, this->size() - N); } - /// \brief Drop everything but the first \p N elements of the array. + /// \brief Return a copy of *this with only the first \p N elements. LLVM_ATTRIBUTE_UNUSED_RESULT - MutableArrayRef keep_front(size_t N = 1) const { + MutableArrayRef take_front(size_t N = 1) const { if (N >= this->size()) return *this; return drop_back(this->size() - N); } - /// \brief Drop everything but the last \p N elements of the array. + /// \brief Return a copy of *this with only the last \p N elements. LLVM_ATTRIBUTE_UNUSED_RESULT - MutableArrayRef keep_back(size_t N = 1) const { + MutableArrayRef take_back(size_t N = 1) const { if (N >= this->size()) return *this; return drop_front(this->size() - N); From f08685310ef9a3a2d088bc8429b098501a3f86fb Mon Sep 17 00:00:00 2001 From: Honggyu Kim Date: Thu, 1 Sep 2016 11:44:06 +0000 Subject: [PATCH 05/23] [IR] Properly handle escape characters in Attribute::getAsString() If an attribute name has special characters such as '\01', it is not properly printed in LLVM assembly language format. Since the format expects the special characters are printed as it is, it has to contain escape characters to make it printable. Before: attributes #0 = { ... "counting-function"="^A__gnu_mcount_nc" ... After: attributes #0 = { ... "counting-function"="\01__gnu_mcount_nc" ... Reviewers: hfinkel, rengolin, rjmccall, compnerd Subscribers: nemanjai, mcrosier, hans, shenhan, majnemer, llvm-commits Differential Revision: https://reviews.llvm.org/D23792 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280357 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 89ea36c5e1ce64cdc6e2e6229e2619d25121f511) --- include/llvm/ADT/StringExtras.h | 5 +++++ lib/IR/AsmWriter.cpp | 4 +--- lib/IR/Attributes.cpp | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/llvm/ADT/StringExtras.h b/include/llvm/ADT/StringExtras.h index bdbb4d3f593..05513e149be 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,10 @@ 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); + template inline std::string join_impl(IteratorT Begin, IteratorT End, StringRef Separator, std::input_iterator_tag) { 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; } From 3af3da0ef2fa59a1901ed8f0a1dbfe47dd4fb938 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Mon, 19 Sep 2016 15:34:51 +0000 Subject: [PATCH 06/23] [Support] Add StringRef::withNullAsEmpty() When porting large bodies of code from using const char* to StringRef, it is helpful to be able to treat nullptr as an empty string, since that it is often what it is used to indicate in C-style code. Differential Revision: https://reviews.llvm.org/D24697 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281906 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 781fb6ecc8e89b36ed03ad41087e9ce34d854507) --- include/llvm/ADT/StringRef.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index f18cf020416..00d70ce9589 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -88,6 +88,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 /// @{ From cd4d5a7bc409290b272a4aec0317a4654a02a4df Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 21 Sep 2016 22:29:36 +0000 Subject: [PATCH 07/23] =delete the StringRef(nullptr_t) constructor. It's a guaranteed crash if you construct a StringRef with nullptr, so might as well delete the constructor that allows it. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282116 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ecdd589ea6470e8496014686d9b74dc5a7a342a1) --- include/llvm/ADT/StringRef.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index 00d70ce9589..f8c0b8dcd7e 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -68,6 +68,8 @@ namespace llvm { /// Construct an empty string ref. /*implicit*/ StringRef() : Data(nullptr), Length(0) {} + StringRef(std::nullptr_t) = delete; + /// Construct a string ref from a cstring. /*implicit*/ StringRef(const char *Str) : Data(Str) { From 8c4f3136a4567899080895483a68fc7180f78f16 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 22 Sep 2016 15:05:19 +0000 Subject: [PATCH 08/23] [Support] Add StringRef::consumeInteger. StringRef::getInteger() exists and treats the entire string as an integer of the specified radix, failing if any invalid characters are encountered or the number overflows. Sometimes you might have something like "123456foo" and you want to get the number 123456 and leave the string "foo" remaining. This is similar to what would be possible by using the standard runtime library functions strtoul et al and specifying an end pointer. This patch adds consumeInteger(), which does exactly that. It consumes as much as possible until an invalid character is found, and modifies the StringRef in place so that upon return only the portion of the StringRef after the number remains. Differential Revision: https://reviews.llvm.org/D24778 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282164 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fb27f55135bed1e993b58e849c1930268a64204c) --- include/llvm/ADT/StringRef.h | 35 +++++++ lib/Support/StringRef.cpp | 80 ++++++++++----- unittests/ADT/StringRefTest.cpp | 177 ++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 25 deletions(-) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index f8c0b8dcd7e..b0dfd7a4cc3 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -32,6 +32,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. /// @@ -392,6 +396,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 diff --git a/lib/Support/StringRef.cpp b/lib/Support/StringRef.cpp index 8a9da5edca8..7503fac240a 100644 --- a/lib/Support/StringRef.cpp +++ b/lib/Support/StringRef.cpp @@ -366,17 +366,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 +384,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 +437,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/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 97ddaf6adcc..4249f231481 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -590,6 +590,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"; From 89eccd7c72cba0a17f9d002ed72c2b1fbe7f0c6c Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 22 Sep 2016 15:55:05 +0000 Subject: [PATCH 09/23] Speculative fix for build failures due to consumeInteger. A recent patch added support for consumeInteger() and made getAsInteger delegate to this function. A few buildbots are failing as a result with an assertion failure. On a hunch, I tested what happens if I call getAsInteger() on an empty string, and sure enough it crashes the same way that the buildbots are crashing. I confirmed that getAsInteger() on an empty string did not crash before my patch, so I suspect this to be the cause. I also added a unit test for the empty string. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282170 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 20bb0322fd381dd7e265eaa05b7fb1c292278abe) --- lib/Support/StringRef.cpp | 3 +++ unittests/ADT/StringRefTest.cpp | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Support/StringRef.cpp b/lib/Support/StringRef.cpp index 7503fac240a..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; diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 4249f231481..40ab4e038b8 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 From 6442008c53e5c6fcbd90108f4450bbadc9a89a95 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 22 Sep 2016 19:21:32 +0000 Subject: [PATCH 10/23] Fix build breakage due to typo in cast. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282183 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 09666276570c2c31f6752a41dd2a16f7c79b5461) --- include/llvm/ADT/StringRef.h | 2 +- unittests/ADT/StringRefTest.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index b0dfd7a4cc3..0c0fa5059d3 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -421,7 +421,7 @@ namespace llvm { consumeInteger(unsigned Radix, T &Result) { unsigned long long ULLVal; if (consumeUnsignedInteger(*this, Radix, ULLVal) || - static_cast(static_cast(ULLVal)) != ULLVal) + static_cast(static_cast(ULLVal)) != ULLVal) return true; Result = ULLVal; return false; diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 40ab4e038b8..39ed71b77e9 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -580,6 +580,8 @@ static const char* BadStrings[] = { , "08" // illegal oct characters , "0o8" // illegal oct characters , "-123" // negative unsigned value + , "0x" + , "0b" }; From 41760b06db686d2df91e209690c15b5665aa904a Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Sun, 25 Sep 2016 03:27:29 +0000 Subject: [PATCH 11/23] Add some predicated searching functions to StringRef. This adds 4 new functions to StringRef, which can be used to take or drop characters while a certain condition is met, or until a certain condition is met. They are: take_while - Return characters until a condition is not met. take_until - Return characters until a condition is met. drop_while - Remove characters until a condition is not met. drop_until - Remove characters until a condition is met. Internally, all of these functions delegate to two additional helper functions which can be used to search for the position of a character meeting or not meeting a condition, which are: find_if - Find the first character matching a predicate. find_if_not - Find the first character not matching a predicate. Differential Revision: https://reviews.llvm.org/D24842 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282346 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 568a8f4a4de56dd51a118dd7559b497cee8f8b96) --- include/llvm/ADT/StringRef.h | 67 +++++++++++++++++++++++++++++++++ unittests/ADT/StringRefTest.cpp | 56 +++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index 0c0fa5059d3..f6a348e8ab4 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 @@ -276,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 @@ -347,6 +374,14 @@ 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; } + + LLVM_ATTRIBUTE_ALWAYS_INLINE + bool contains(char C) const { return find_first_of(C) != npos; } + /// @} /// @name Helpful Algorithms /// @{ @@ -491,6 +526,22 @@ namespace llvm { 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 @@ -509,6 +560,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/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index 39ed71b77e9..e1f54967572 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -864,4 +864,60 @@ TEST(StringRefTest, Take) { 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(4, Punct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, NoPunct.find_if(IsPunct)); + EXPECT_EQ(StringRef::npos, Empty.find_if(IsPunct)); + + EXPECT_EQ(4, 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 From e96242b4b07480ffdc068528094d8857a641d07a Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Sun, 25 Sep 2016 03:57:34 +0000 Subject: [PATCH 12/23] Fix signed / unsigned comparison. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282348 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3ae26e93759c69c35749e23a89a6b205886b28fc) --- unittests/ADT/StringRefTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/ADT/StringRefTest.cpp b/unittests/ADT/StringRefTest.cpp index e1f54967572..ca6b011d695 100644 --- a/unittests/ADT/StringRefTest.cpp +++ b/unittests/ADT/StringRefTest.cpp @@ -871,11 +871,11 @@ TEST(StringRefTest, FindIf) { auto IsPunct = [](char c) { return ::ispunct(c); }; auto IsAlpha = [](char c) { return ::isalpha(c); }; - EXPECT_EQ(4, Punct.find_if(IsPunct)); + 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(4, Punct.find_if_not(IsAlpha)); + 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)); } From 962cff594a7ddc6dd0888a19fbe179f84cab726e Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Sun, 25 Sep 2016 04:06:39 +0000 Subject: [PATCH 13/23] Add a comment on StringRef::contains(char) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282350 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9f072db19bc4f674b85c936c7983212c4034030d) --- include/llvm/ADT/StringRef.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index f6a348e8ab4..d3683c79e50 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -379,6 +379,8 @@ namespace llvm { 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; } From be1750793b49e43a3134ce0bd3ed518ef8eb6a5e Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Mon, 26 Sep 2016 20:08:05 +0000 Subject: [PATCH 14/23] Allow StringRef to be constructed from a null pointer. Differential Revision: https://reviews.llvm.org/D24904 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282433 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit bb748a19aecc4955d12185a8a7922640654565ee) --- include/llvm/ADT/StringRef.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/llvm/ADT/StringRef.h b/include/llvm/ADT/StringRef.h index d3683c79e50..a053b414a10 100644 --- a/include/llvm/ADT/StringRef.h +++ b/include/llvm/ADT/StringRef.h @@ -73,14 +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 From b769d83ccad6e319ef73e1fa8d2623f44bd596ee Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Tue, 27 Sep 2016 16:37:30 +0000 Subject: [PATCH 15/23] Add llvm::join_items to StringExtras. llvm::join_items is similar to llvm::join, which produces a string by concatenating a sequence of values together separated by a given separator. But it differs in that the arguments to llvm::join() are same-type members of a container, whereas the arguments to llvm::join_items are arbitrary types passed into a variadic template. The only requirement on parameters to llvm::join_items (including for the separator themselves) is that they be implicitly convertible to std::string or have an overload of std::string::operator+ Differential Revision: https://reviews.llvm.org/D24880 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282502 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b93468a311e3c72cff75c6d65421fb392d231939) --- include/llvm/ADT/StringExtras.h | 56 +++++++++++++++++++++++++++++- unittests/ADT/CMakeLists.txt | 1 + unittests/ADT/StringExtrasTest.cpp | 52 +++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 unittests/ADT/StringExtrasTest.cpp diff --git a/include/llvm/ADT/StringExtras.h b/include/llvm/ADT/StringExtras.h index 05513e149be..488748a5f60 100644 --- a/include/llvm/ADT/StringExtras.h +++ b/include/llvm/ADT/StringExtras.h @@ -155,6 +155,8 @@ static inline StringRef getOrdinalSuffix(unsigned Val) { /// 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) { @@ -189,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/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index a4c68fb2aab..c33ce4121c7 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -45,6 +45,7 @@ set(ADTSources SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StringExtrasTest.cpp StringMapTest.cpp StringRefTest.cpp TinyPtrVectorTest.cpp 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)); +} From 9f78d67d3aa0f54b9955160d78c01c3d21c7cfd9 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 29 Sep 2016 22:59:30 +0000 Subject: [PATCH 16/23] Add llvm::enumerate() to STLExtras. enumerate allows you to iterate over a range by pairing the iterator's value with its index in the enumeration. This gives you most of the benefits of using a for loop while still allowing the range syntax. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282804 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ab82a456ef39cb25d71501f019738ae7ed9e59a7) --- include/llvm/ADT/STLExtras.h | 67 +++++++++++++++++++++++++++++++++ unittests/ADT/STLExtrasTest.cpp | 49 ++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 821f1cc24d1..ce3b57c4eff 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -613,6 +613,73 @@ template struct deref { } }; +namespace detail { +template class enumerator_impl { +public: + template struct result_pair { + result_pair(std::size_t Index, V Value) : Index(Index), Value(Value) {} + + const std::size_t Index; + V Value; + }; + + template struct iterator { + iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {} + + result_pair operator*() const { + return result_pair(Index, *Iter); + } + result_pair operator*() { return result_pair(Index, *Iter); } + + iterator &operator++() { + ++Iter; + ++Index; + return *this; + } + + bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; } + + private: + I Iter; + std::size_t Index; + }; + + enumerator_impl(I Begin, I End) + : Begin(std::move(Begin)), End(std::move(End)) {} + + iterator begin() { return iterator(Begin, 0); } + iterator end() { return iterator(End, std::size_t(-1)); } + + iterator begin() const { return iterator(Begin, 0); } + iterator end() const { return iterator(End, std::size_t(-1)); } + +private: + I Begin; + I End; +}; +} + +/// 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 auto enumerate(R &&Range) { + typedef decltype(std::begin(Range)) I; + typedef decltype(*std::begin(Range)) V; + return detail::enumerator_impl(std::begin(Range), std::end(Range)); +} + } // End llvm namespace #endif diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index dc62b03741c..790cf1c9b61 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -10,6 +10,8 @@ #include "llvm/ADT/STLExtras.h" #include "gtest/gtest.h" +#include + using namespace llvm; namespace { @@ -37,4 +39,51 @@ TEST(STLExtrasTest, Rank) { EXPECT_EQ(4, f(rank<6>())); } +TEST(STLExtrasTest, Enumerate) { + std::vector foo = {'a', 'b', 'c'}; + + std::vector> results; + + for (auto X : llvm::enumerate(foo)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + ASSERT_EQ(3, results.size()); + EXPECT_EQ(0, results[0].first); + EXPECT_EQ('a', results[0].second); + EXPECT_EQ(1, results[1].first); + EXPECT_EQ('b', results[1].second); + EXPECT_EQ(2, results[2].first); + EXPECT_EQ('c', results[2].second); + + results.clear(); + const std::vector bar = {'1', '2', '3'}; + for (auto X : llvm::enumerate(bar)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + EXPECT_EQ(0, results[0].first); + EXPECT_EQ('1', results[0].second); + EXPECT_EQ(1, results[1].first); + EXPECT_EQ('2', results[1].second); + EXPECT_EQ(2, results[2].first); + EXPECT_EQ('3', results[2].second); + + results.clear(); + const std::vector baz; + for (auto X : llvm::enumerate(baz)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + EXPECT_TRUE(baz.empty()); +} + +TEST(STLExtrasTest, EnumerateModify) { + 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]); +} } From 6a2627b4441f07f249a6d932bd249f7975d53b17 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 29 Sep 2016 23:05:41 +0000 Subject: [PATCH 17/23] Revert "Add llvm::enumerate() to STLExtras." This reverts commit r282804 as it seems to use some C++ features that not all compilers support. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282809 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a4814510ec0277480519fbe19580bb6e81e2bf0c) --- include/llvm/ADT/STLExtras.h | 67 --------------------------------- unittests/ADT/STLExtrasTest.cpp | 49 ------------------------ 2 files changed, 116 deletions(-) diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index ce3b57c4eff..821f1cc24d1 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -613,73 +613,6 @@ template struct deref { } }; -namespace detail { -template class enumerator_impl { -public: - template struct result_pair { - result_pair(std::size_t Index, V Value) : Index(Index), Value(Value) {} - - const std::size_t Index; - V Value; - }; - - template struct iterator { - iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {} - - result_pair operator*() const { - return result_pair(Index, *Iter); - } - result_pair operator*() { return result_pair(Index, *Iter); } - - iterator &operator++() { - ++Iter; - ++Index; - return *this; - } - - bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; } - - private: - I Iter; - std::size_t Index; - }; - - enumerator_impl(I Begin, I End) - : Begin(std::move(Begin)), End(std::move(End)) {} - - iterator begin() { return iterator(Begin, 0); } - iterator end() { return iterator(End, std::size_t(-1)); } - - iterator begin() const { return iterator(Begin, 0); } - iterator end() const { return iterator(End, std::size_t(-1)); } - -private: - I Begin; - I End; -}; -} - -/// 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 auto enumerate(R &&Range) { - typedef decltype(std::begin(Range)) I; - typedef decltype(*std::begin(Range)) V; - return detail::enumerator_impl(std::begin(Range), std::end(Range)); -} - } // End llvm namespace #endif diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index 790cf1c9b61..dc62b03741c 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -10,8 +10,6 @@ #include "llvm/ADT/STLExtras.h" #include "gtest/gtest.h" -#include - using namespace llvm; namespace { @@ -39,51 +37,4 @@ TEST(STLExtrasTest, Rank) { EXPECT_EQ(4, f(rank<6>())); } -TEST(STLExtrasTest, Enumerate) { - std::vector foo = {'a', 'b', 'c'}; - - std::vector> results; - - for (auto X : llvm::enumerate(foo)) { - results.push_back(std::make_pair(X.Index, X.Value)); - } - ASSERT_EQ(3, results.size()); - EXPECT_EQ(0, results[0].first); - EXPECT_EQ('a', results[0].second); - EXPECT_EQ(1, results[1].first); - EXPECT_EQ('b', results[1].second); - EXPECT_EQ(2, results[2].first); - EXPECT_EQ('c', results[2].second); - - results.clear(); - const std::vector bar = {'1', '2', '3'}; - for (auto X : llvm::enumerate(bar)) { - results.push_back(std::make_pair(X.Index, X.Value)); - } - EXPECT_EQ(0, results[0].first); - EXPECT_EQ('1', results[0].second); - EXPECT_EQ(1, results[1].first); - EXPECT_EQ('2', results[1].second); - EXPECT_EQ(2, results[2].first); - EXPECT_EQ('3', results[2].second); - - results.clear(); - const std::vector baz; - for (auto X : llvm::enumerate(baz)) { - results.push_back(std::make_pair(X.Index, X.Value)); - } - EXPECT_TRUE(baz.empty()); -} - -TEST(STLExtrasTest, EnumerateModify) { - 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]); -} } From 94e2b94e99b3498501bced911d4bbadc76ad7086 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Fri, 30 Sep 2016 15:43:59 +0000 Subject: [PATCH 18/23] Resubmit "Add llvm::enumerate() to STLExtras." The CL was originally failing due to the use of some C++14 specific features, so I've removed those. Hopefully this will satisfy the bots. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282867 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f216a86852769a96263eca6c9227079ff8816f7a) --- include/llvm/ADT/STLExtras.h | 72 +++++++++++++++++++++++++++++++++ unittests/ADT/STLExtrasTest.cpp | 49 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 821f1cc24d1..8a1c4d4d127 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -613,6 +613,78 @@ 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; + }; + + struct iterator { + iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {} + + result_pair operator*() const { + return result_pair(Index, *Iter); + } + result_pair operator*() { return result_pair(Index, *Iter); } + + iterator &operator++() { + ++Iter; + ++Index; + return *this; + } + + bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; } + + private: + I Iter; + std::size_t Index; + }; + + enumerator_impl(I Begin, I End) + : Begin(std::move(Begin)), End(std::move(End)) {} + + iterator begin() { return iterator(Begin, 0); } + iterator end() { return iterator(End, std::size_t(-1)); } + + iterator begin() const { return iterator(Begin, 0); } + iterator end() const { return iterator(End, std::size_t(-1)); } + +private: + I Begin; + I End; +}; + +template +auto make_enumerator(I Begin, I End) -> enumerator_impl { + return enumerator_impl(std::move(Begin), std::move(End)); +} +} + +/// 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 +auto enumerate(R &&Range) + -> decltype(detail::make_enumerator(std::begin(Range), std::end(Range))) { + return detail::make_enumerator(std::begin(Range), std::end(Range)); +} + } // End llvm namespace #endif diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index dc62b03741c..ebb119600c7 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -10,6 +10,8 @@ #include "llvm/ADT/STLExtras.h" #include "gtest/gtest.h" +#include + using namespace llvm; namespace { @@ -37,4 +39,51 @@ TEST(STLExtrasTest, Rank) { EXPECT_EQ(4, f(rank<6>())); } +TEST(STLExtrasTest, Enumerate) { + std::vector foo = {'a', 'b', 'c'}; + + std::vector> results; + + for (auto X : llvm::enumerate(foo)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + ASSERT_EQ(3u, results.size()); + EXPECT_EQ(0u, results[0].first); + EXPECT_EQ('a', results[0].second); + EXPECT_EQ(1u, results[1].first); + EXPECT_EQ('b', results[1].second); + EXPECT_EQ(2u, results[2].first); + EXPECT_EQ('c', results[2].second); + + results.clear(); + const std::vector bar = {'1', '2', '3'}; + for (auto X : llvm::enumerate(bar)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + EXPECT_EQ(0u, results[0].first); + EXPECT_EQ('1', results[0].second); + EXPECT_EQ(1u, results[1].first); + EXPECT_EQ('2', results[1].second); + EXPECT_EQ(2u, results[2].first); + EXPECT_EQ('3', results[2].second); + + results.clear(); + const std::vector baz; + for (auto X : llvm::enumerate(baz)) { + results.push_back(std::make_pair(X.Index, X.Value)); + } + EXPECT_TRUE(baz.empty()); +} + +TEST(STLExtrasTest, EnumerateModify) { + 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]); +} } From ea906a2de8ebf18f55747c57d0621db1b1628f20 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 5 Oct 2016 16:54:09 +0000 Subject: [PATCH 19/23] Add llvm::enumerate() range adapter. This allows you to enumerate over a range using a range-based for while the return type contains the index of the enumeration. Differential revision: https://reviews.llvm.org/D25124 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283337 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5844b06a17eaecc1f460505539f4d4fa654b3752) --- include/llvm/ADT/STLExtras.h | 46 ++++------ unittests/ADT/STLExtrasTest.cpp | 155 ++++++++++++++++++++++++++------ 2 files changed, 147 insertions(+), 54 deletions(-) diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index 8a1c4d4d127..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 @@ -614,7 +614,7 @@ template struct deref { }; namespace detail { -template class enumerator_impl { +template class enumerator_impl { public: template struct result_pair { result_pair(std::size_t Index, X Value) : Index(Index), Value(Value) {} @@ -623,13 +623,16 @@ template class enumerator_impl { X Value; }; - struct iterator { - iterator(I Iter, std::size_t Index) : Iter(Iter), Index(Index) {} + class iterator { + typedef + typename std::iterator_traits>::reference iter_reference; + typedef result_pair result_type; - result_pair operator*() const { - return result_pair(Index, *Iter); - } - result_pair operator*() { return result_pair(Index, *Iter); } + public: + iterator(IterOfRange &&Iter, std::size_t Index) + : Iter(Iter), Index(Index) {} + + result_type operator*() const { return result_type(Index, *Iter); } iterator &operator++() { ++Iter; @@ -640,28 +643,19 @@ template class enumerator_impl { bool operator!=(const iterator &RHS) const { return Iter != RHS.Iter; } private: - I Iter; + IterOfRange Iter; std::size_t Index; }; - enumerator_impl(I Begin, I End) - : Begin(std::move(Begin)), End(std::move(End)) {} - - iterator begin() { return iterator(Begin, 0); } - iterator end() { return iterator(End, std::size_t(-1)); } +public: + explicit enumerator_impl(R &&Range) : Range(std::forward(Range)) {} - iterator begin() const { return iterator(Begin, 0); } - iterator end() const { return iterator(End, std::size_t(-1)); } + iterator begin() { return iterator(std::begin(Range), 0); } + iterator end() { return iterator(std::end(Range), std::size_t(-1)); } private: - I Begin; - I End; + R Range; }; - -template -auto make_enumerator(I Begin, I End) -> enumerator_impl { - return enumerator_impl(std::move(Begin), std::move(End)); -} } /// Given an input range, returns a new range whose values are are pair (A,B) @@ -679,10 +673,8 @@ auto make_enumerator(I Begin, I End) -> enumerator_impl { /// Item 2 - C /// Item 3 - D /// -template -auto enumerate(R &&Range) - -> decltype(detail::make_enumerator(std::begin(Range), std::end(Range))) { - return detail::make_enumerator(std::begin(Range), std::end(Range)); +template detail::enumerator_impl enumerate(R &&Range) { + return detail::enumerator_impl(std::forward(Range)); } } // End llvm namespace diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index ebb119600c7..e5ac1800d2f 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -39,51 +39,152 @@ TEST(STLExtrasTest, Rank) { EXPECT_EQ(4, f(rank<6>())); } -TEST(STLExtrasTest, Enumerate) { +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'}; - - std::vector> results; + std::vector> CharResults; for (auto X : llvm::enumerate(foo)) { - results.push_back(std::make_pair(X.Index, X.Value)); + CharResults.emplace_back(X.Index, X.Value); } - ASSERT_EQ(3u, results.size()); - EXPECT_EQ(0u, results[0].first); - EXPECT_EQ('a', results[0].second); - EXPECT_EQ(1u, results[1].first); - EXPECT_EQ('b', results[1].second); - EXPECT_EQ(2u, results[2].first); - EXPECT_EQ('c', results[2].second); - - results.clear(); - const std::vector bar = {'1', '2', '3'}; + ASSERT_EQ(3u, CharResults.size()); + EXPECT_EQ(std::make_pair(0u, 'a'), CharResults[0]); + EXPECT_EQ(std::make_pair(1u, 'b'), CharResults[1]); + EXPECT_EQ(std::make_pair(2u, 'c'), CharResults[2]); + + // Test a const range of a different type. + std::vector> IntResults; + const std::vector bar = {1, 2, 3}; for (auto X : llvm::enumerate(bar)) { - results.push_back(std::make_pair(X.Index, X.Value)); + IntResults.emplace_back(X.Index, X.Value); } - EXPECT_EQ(0u, results[0].first); - EXPECT_EQ('1', results[0].second); - EXPECT_EQ(1u, results[1].first); - EXPECT_EQ('2', results[1].second); - EXPECT_EQ(2u, results[2].first); - EXPECT_EQ('3', results[2].second); - - results.clear(); + ASSERT_EQ(3u, IntResults.size()); + EXPECT_EQ(std::make_pair(0u, 1), IntResults[0]); + EXPECT_EQ(std::make_pair(1u, 2), IntResults[1]); + EXPECT_EQ(std::make_pair(2u, 3), IntResults[2]); + + // Test an empty range. + IntResults.clear(); const std::vector baz; for (auto X : llvm::enumerate(baz)) { - results.push_back(std::make_pair(X.Index, X.Value)); + IntResults.emplace_back(X.Index, X.Value); } - EXPECT_TRUE(baz.empty()); + EXPECT_TRUE(IntResults.empty()); } -TEST(STLExtrasTest, EnumerateModify) { +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. + 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(std::make_pair(0u, 1), Results[0]); + EXPECT_EQ(std::make_pair(1u, 2), Results[1]); + EXPECT_EQ(std::make_pair(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. + 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(std::make_pair(0u, '2'), Results[0]); + EXPECT_EQ(std::make_pair(1u, '3'), Results[1]); + EXPECT_EQ(std::make_pair(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; + CanCopy(CanCopy &&) = default; +}; + +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); +} } From c1b9e51842401e74c3e0f5b1ea7efd04fe69c781 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 5 Oct 2016 17:04:36 +0000 Subject: [PATCH 20/23] Fix build due to comparison of std::pairs. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283342 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e53904ebe71ad087ed9f73b476bd2720e7cce158) --- unittests/ADT/STLExtrasTest.cpp | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index e5ac1800d2f..849b6f1d097 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -43,26 +43,28 @@ 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'}; - std::vector> CharResults; + 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(std::make_pair(0u, 'a'), CharResults[0]); - EXPECT_EQ(std::make_pair(1u, 'b'), CharResults[1]); - EXPECT_EQ(std::make_pair(2u, 'c'), CharResults[2]); + 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. - std::vector> IntResults; + 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(std::make_pair(0u, 1), IntResults[0]); - EXPECT_EQ(std::make_pair(1u, 2), IntResults[1]); - EXPECT_EQ(std::make_pair(2u, 3), IntResults[2]); + 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(); @@ -88,7 +90,8 @@ TEST(STLExtrasTest, EnumerateModifyLValue) { TEST(STLExtrasTest, EnumerateRValueRef) { // Test that an rvalue can be enumerated. - std::vector> Results; + typedef std::pair PairType; + std::vector Results; auto Enumerator = llvm::enumerate(std::vector{1, 2, 3}); @@ -97,16 +100,17 @@ TEST(STLExtrasTest, EnumerateRValueRef) { } ASSERT_EQ(3u, Results.size()); - EXPECT_EQ(std::make_pair(0u, 1), Results[0]); - EXPECT_EQ(std::make_pair(1u, 2), Results[1]); - EXPECT_EQ(std::make_pair(2u, 3), Results[2]); + 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. - std::vector> Results; + typedef std::pair PairType; + std::vector Results; for (auto X : llvm::enumerate(std::vector{'1', '2', '3'})) { ++X.Value; @@ -114,9 +118,9 @@ TEST(STLExtrasTest, EnumerateModifyRValue) { } ASSERT_EQ(3u, Results.size()); - EXPECT_EQ(std::make_pair(0u, '2'), Results[0]); - EXPECT_EQ(std::make_pair(1u, '3'), Results[1]); - EXPECT_EQ(std::make_pair(2u, '4'), Results[2]); + EXPECT_EQ(PairType(0u, '2'), Results[0]); + EXPECT_EQ(PairType(1u, '3'), Results[1]); + EXPECT_EQ(PairType(2u, '4'), Results[2]); } template struct CanMove {}; From dac6b09cc8e246c6a90c2c680527ae79e0b7c183 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 5 Oct 2016 21:44:46 +0000 Subject: [PATCH 21/23] Fix the build with MSVC 2013, still cannot default move ctors yet Ten days. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283394 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 87bf0196f2d0dac7a2bad45eb457e647afbed9e7) --- unittests/ADT/STLExtrasTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index 849b6f1d097..4f49fb77479 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -136,7 +136,8 @@ template <> struct CanCopy { CanCopy(const CanCopy &) = delete; CanCopy() = default; - CanCopy(CanCopy &&) = default; + // FIXME: Use '= default' when we drop MSVC 2013. + CanCopy(CanCopy &&) {}; }; template From 3e3953aeae38a406c6bbb15444b30fec39d5bd69 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 5 Oct 2016 21:46:56 +0000 Subject: [PATCH 22/23] Remove extra semicolon git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283395 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 81e3914a008a38f0636dc576b34b3de126cc40d2) --- unittests/ADT/STLExtrasTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/ADT/STLExtrasTest.cpp b/unittests/ADT/STLExtrasTest.cpp index 4f49fb77479..db344bc6089 100644 --- a/unittests/ADT/STLExtrasTest.cpp +++ b/unittests/ADT/STLExtrasTest.cpp @@ -137,7 +137,7 @@ template <> struct CanCopy { CanCopy() = default; // FIXME: Use '= default' when we drop MSVC 2013. - CanCopy(CanCopy &&) {}; + CanCopy(CanCopy &&) {} }; template From f5d747c4419969fa9a71594782ad4918456151ef Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 11 Oct 2016 20:39:16 +0000 Subject: [PATCH 23/23] Re-apply "Disallow ArrayRef assignment from temporaries." This re-applies r283798, disabled in r283803, with the static_assert tests disabled under MSVC. The deleted functions still seem to catch mistakes in MSVC, so it's not a significant loss. Part of rdar://problem/16375365 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@283935 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8f3f50aeef0afa4b74d51713963d42b6dd9f22ae) --- include/llvm/ADT/ArrayRef.h | 16 ++++++++++++++++ unittests/ADT/ArrayRefTest.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/llvm/ADT/ArrayRef.h b/include/llvm/ADT/ArrayRef.h index c1d66c69903..3efc09ddd36 100644 --- a/include/llvm/ADT/ArrayRef.h +++ b/include/llvm/ADT/ArrayRef.h @@ -219,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 /// @{ diff --git a/unittests/ADT/ArrayRefTest.cpp b/unittests/ADT/ArrayRefTest.cpp index 840d53f786b..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) { @@ -160,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};