From 7e117aec6b48663343e5b84672c5f3357cfa6a0d Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Sat, 17 Dec 2016 00:38:15 +0000 Subject: [PATCH] Add support for formatv to llvm::Twine. Differential Revision: https://reviews.llvm.org/D27835 llvm-svn: 290020 --- include/llvm/ADT/Twine.h | 12 ++++++++++++ lib/Support/Twine.cpp | 12 ++++++++++++ unittests/ADT/TwineTest.cpp | 28 +++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/include/llvm/ADT/Twine.h b/include/llvm/ADT/Twine.h index 47caf46f7c2..f5f00dcfafe 100644 --- a/include/llvm/ADT/Twine.h +++ b/include/llvm/ADT/Twine.h @@ -19,6 +19,7 @@ namespace llvm { + class formatv_object_base; class raw_ostream; /// Twine - A lightweight data structure for efficiently representing the @@ -102,6 +103,9 @@ namespace llvm { /// A pointer to a SmallString instance. SmallStringKind, + /// A pointer to a formatv_object_base instance. + FormatvObjectKind, + /// A char value, to render as a character. CharKind, @@ -137,6 +141,7 @@ namespace llvm { const std::string *stdString; const StringRef *stringRef; const SmallVectorImpl *smallString; + const formatv_object_base *formatvObject; char character; unsigned int decUI; int decI; @@ -290,6 +295,13 @@ namespace llvm { assert(isValid() && "Invalid twine!"); } + /// Construct from a formatv_object_base. + /*implicit*/ Twine(const formatv_object_base &Fmt) + : LHSKind(FormatvObjectKind), RHSKind(EmptyKind) { + LHS.formatvObject = &Fmt; + assert(isValid() && "Invalid twine!"); + } + /// Construct from a char. explicit Twine(char Val) : LHSKind(CharKind), RHSKind(EmptyKind) { diff --git a/lib/Support/Twine.cpp b/lib/Support/Twine.cpp index 5e989fb28b4..465c6e6b8c4 100644 --- a/lib/Support/Twine.cpp +++ b/lib/Support/Twine.cpp @@ -10,6 +10,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -18,6 +19,11 @@ std::string Twine::str() const { if (LHSKind == StdStringKind && RHSKind == EmptyKind) return *LHS.stdString; + // If we're storing a formatv_object, we can avoid an extra copy by formatting + // it immediately and returning the result. + if (LHSKind == FormatvObjectKind && RHSKind == EmptyKind) + return LHS.formatvObject->str(); + // Otherwise, flatten and copy the contents first. SmallString<256> Vec; return toStringRef(Vec).str(); @@ -68,6 +74,9 @@ void Twine::printOneChild(raw_ostream &OS, Child Ptr, case Twine::SmallStringKind: OS << *Ptr.smallString; break; + case Twine::FormatvObjectKind: + OS << *Ptr.formatvObject; + break; case Twine::CharKind: OS << Ptr.character; break; @@ -121,6 +130,9 @@ void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr, case Twine::SmallStringKind: OS << "smallstring:\"" << *Ptr.smallString << "\""; break; + case Twine::FormatvObjectKind: + OS << "formatv:\"" << *Ptr.formatvObject << "\""; + break; case Twine::CharKind: OS << "char:\"" << Ptr.character << "\""; break; diff --git a/unittests/ADT/TwineTest.cpp b/unittests/ADT/TwineTest.cpp index 9683e97511b..8d3b6e3d54a 100644 --- a/unittests/ADT/TwineTest.cpp +++ b/unittests/ADT/TwineTest.cpp @@ -7,8 +7,10 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/Twine.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace llvm; @@ -30,6 +32,7 @@ TEST(TwineTest, Construction) { EXPECT_EQ("hi", Twine(StringRef(std::string("hi"))).str()); EXPECT_EQ("hi", Twine(StringRef("hithere", 2)).str()); EXPECT_EQ("hi", Twine(SmallString<4>("hi")).str()); + EXPECT_EQ("hi", Twine(formatv("{0}", "hi")).str()); } TEST(TwineTest, Numbers) { @@ -65,6 +68,10 @@ TEST(TwineTest, Concat) { repr(Twine().concat(Twine("hi")))); EXPECT_EQ("(Twine smallstring:\"hi\" empty)", repr(Twine().concat(Twine(SmallString<5>("hi"))))); + EXPECT_EQ("(Twine formatv:\"howdy\" empty)", + repr(Twine(formatv("howdy")).concat(Twine()))); + EXPECT_EQ("(Twine formatv:\"howdy\" empty)", + repr(Twine().concat(Twine(formatv("howdy"))))); EXPECT_EQ("(Twine smallstring:\"hey\" cstring:\"there\")", repr(Twine(SmallString<7>("hey")).concat(Twine("there")))); @@ -89,6 +96,25 @@ TEST(TwineTest, toNullTerminatedStringRef) { EXPECT_EQ(0, *Twine(SmallString<11>("hello")) .toNullTerminatedStringRef(storage) .end()); + EXPECT_EQ(0, *Twine(formatv("{0}{1}", "how", "dy")) + .toNullTerminatedStringRef(storage) + .end()); +} + +TEST(TwineTest, LazyEvaluation) { + struct formatter : FormatAdapter { + explicit formatter(int &Count) : Count(Count), FormatAdapter(0) {} + int &Count; + + void format(raw_ostream &OS, StringRef Style) { ++Count; } + }; + + int Count = 0; + formatter Formatter(Count); + (void)Twine(formatv("{0}", Formatter)); + EXPECT_EQ(0, Count); + (void)Twine(formatv("{0}", Formatter)).str(); + EXPECT_EQ(1, Count); } // I suppose linking in the entire code generator to add a unit test to check