Avoid keeping internal string_views in Twine.
authorSterling Augustine <saugustine@google.com>
Fri, 16 Jul 2021 20:17:16 +0000 (13:17 -0700)
committerSterling Augustine <saugustine@google.com>
Tue, 20 Jul 2021 15:46:53 +0000 (08:46 -0700)
This is a follow-up to https://reviews.llvm.org/D103935

A Twine's internal layout should not depend on which version of the
C++ standard is in use. Dynamically linking binaries compiled with two
different layouts (eg, --std=c++14 vs --std=c++17) ends up
problematic.

This change avoids that issue by immediately converting a
string_view to a pointer-and-length at the cost of an extra eight-bytes
in Twine.

Differential Revision: https://reviews.llvm.org/D106186

llvm/include/llvm/ADT/Twine.h
llvm/lib/Support/Twine.cpp
llvm/unittests/ADT/TwineTest.cpp

index 235b814..0885f9b 100644 (file)
@@ -102,14 +102,14 @@ namespace llvm {
       /// A pointer to a StringRef instance.
       StringRefKind,
 
-#if __cplusplus > 201402L
-      // A pointer to a std::string_view instance.
-      StdStringViewKind,
-#endif
-
       /// A pointer to a SmallString instance.
       SmallStringKind,
 
+      /// A Pointer and Length representation. Used for std::string_view.
+      /// Can't use a StringRef here because they are not trivally
+      /// constructible.
+      PtrAndLengthKind,
+
       /// A pointer to a formatv_object_base instance.
       FormatvObjectKind,
 
@@ -145,11 +145,12 @@ namespace llvm {
     {
       const Twine *twine;
       const char *cString;
+      struct {
+        const char *ptr;
+        size_t length;
+      } ptrAndLength;
       const std::string *stdString;
       const StringRef *stringRef;
-#if __cplusplus > 201402L
-      const std::string_view *stdStringView;
-#endif
       const SmallVectorImpl<char> *smallString;
       const formatv_object_base *formatvObject;
       char character;
@@ -295,10 +296,14 @@ namespace llvm {
     }
 
 #if __cplusplus > 201402L
-    /// Construct from an std::string_view.
+    /// Construct from an std::string_view by converting it to a pointer and
+    /// length.  This handles string_views on a pure API basis, and avoids
+    /// storing one (or a pointer to one) inside a Twine, which avoids problems
+    /// when mixing code compiled under various C++ standards.
     /*implicit*/ Twine(const std::string_view &Str)
-        : LHSKind(StdStringViewKind) {
-      LHS.stdStringView = &Str;
+        : LHSKind(PtrAndLengthKind) {
+      LHS.ptrAndLength.ptr = Str.data();
+      LHS.ptrAndLength.length = Str.length();
       assert(isValid() && "Invalid twine!");
     }
 #endif
@@ -430,11 +435,9 @@ namespace llvm {
       case EmptyKind:
       case CStringKind:
       case StdStringKind:
-#if __cplusplus > 201402L
-      case StdStringViewKind:
-#endif
       case StringRefKind:
       case SmallStringKind:
+      case PtrAndLengthKind:
         return true;
       default:
         return false;
@@ -469,14 +472,12 @@ namespace llvm {
         return StringRef(LHS.cString);
       case StdStringKind:
         return StringRef(*LHS.stdString);
-#if __cplusplus > 201402L
-      case StdStringViewKind:
-        return StringRef(*LHS.stdStringView);
-#endif
       case StringRefKind:
         return *LHS.stringRef;
       case SmallStringKind:
         return StringRef(LHS.smallString->data(), LHS.smallString->size());
+      case PtrAndLengthKind:
+        return StringRef(LHS.ptrAndLength.ptr, LHS.ptrAndLength.length);
       }
     }
 
index c1a254c..5cbf8a0 100644 (file)
@@ -65,14 +65,12 @@ void Twine::printOneChild(raw_ostream &OS, Child Ptr,
   case Twine::CStringKind:
     OS << Ptr.cString;
     break;
+  case Twine::PtrAndLengthKind:
+    OS << StringRef(Ptr.ptrAndLength.ptr, Ptr.ptrAndLength.length);
+    break;
   case Twine::StdStringKind:
     OS << *Ptr.stdString;
     break;
-#if __cplusplus > 201402L
-  case StdStringViewKind:
-    OS << StringRef(*Ptr.stdStringView);
-    break;
-#endif
   case Twine::StringRefKind:
     OS << *Ptr.stringRef;
     break;
@@ -124,15 +122,14 @@ void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr,
     OS << "cstring:\""
        << Ptr.cString << "\"";
     break;
+  case Twine::PtrAndLengthKind:
+    OS << "ptrAndLength:\""
+       << StringRef(Ptr.ptrAndLength.ptr, Ptr.ptrAndLength.length) << "\"";
+    break;
   case Twine::StdStringKind:
     OS << "std::string:\""
        << Ptr.stdString << "\"";
     break;
-#if __cplusplus > 201402L
-  case Twine::StdStringViewKind:
-    OS << "std::string_view:\"" << StringRef(*Ptr.stdStringView) << "\"";
-    break;
-#endif
   case Twine::StringRefKind:
     OS << "stringref:\""
        << Ptr.stringRef << "\"";
index b9d107d..e0fb7e5 100644 (file)
@@ -77,7 +77,7 @@ TEST(TwineTest, Concat) {
   EXPECT_EQ("(Twine smallstring:\"hey\" cstring:\"there\")", 
             repr(Twine(SmallString<7>("hey")).concat(Twine("there"))));
 #if __cplusplus > 201402L
-  EXPECT_EQ("(Twine std::string_view:\"hey\" cstring:\"there\")",
+  EXPECT_EQ("(Twine ptrAndLength:\"hey\" cstring:\"there\")",
             repr(Twine(std::string_view("hey")).concat(Twine("there"))));
 #endif