As the documentation stands, Twines are inherently dangerous and they should only be used as temporary arguments to a function. No temporaries should be stored as in the following
void foo(const Twine &T); ... StringRef X = ... unsigned i = ... const Twine &Tmp = X + "." + Twine(i); foo(Tmp);
anyway there's a different case much more subtle that doesn't seem to create problems at first and (tested on a Win8 machine - VS2012) sometimes only shows up in different project configurations (e.g. Release but not in Debug):
const char *ptr1 = "_text1"; const char *ptr2 = "_text2"; const char *ptr3 = "_text3"; llvm::Twine data = llvm::Twine("data") + ptr1 + ptr2 + ptr3; function_call(data); // function_call(Twine& tw);
This is a totally different case where data consistency is put at risk and only at runtime one might encounter (or not) a quite substantial variety of errors, mostly stack overflows.
The reason is explained by the following C++ test-case which causes a runtime stack-overflow on a Win8 VS2012 system
#include <iostream> using namespace std; class Twine; enum Kind {twineType, stringType, emptyKind}; union Child { const char *cStringPtr; const Twine* twine; }; // Simplified model of the Twine class - relevant excerpts class Twine { public: Child LHS; Kind LHSKind; Child RHS; Kind RHSKind; Twine(const char* pt) { LHSKind = Kind::stringType; LHS.cStringPtr = pt; RHSKind = Kind::emptyKind; } explicit Twine(Child _LHS, Kind _LHSKind, Child _RHS, Kind _RHSKind) : LHS(_LHS), RHS(_RHS), LHSKind(_LHSKind), RHSKind(_RHSKind) { } Twine Twine::concat(const Twine &Suffix) const { Child NewLHS, NewRHS; NewLHS.twine = this; NewRHS.twine = &Suffix; Kind NewLHSKind = Kind::twineType, NewRHSKind = Kind::twineType; if(RHSKind == Kind::emptyKind) { // If LHS is unused, just copy the POD stuff and forget about the object (save indirection) NewLHS = LHS; NewLHSKind = LHSKind; } if(Suffix.RHSKind == Kind::emptyKind) { // If RHS is unused, just copy the POD stuff and forget about the object (save indirection) NewRHS = Suffix.LHS; NewRHSKind = Suffix.LHSKind; } return Twine(NewLHS, NewLHSKind, NewRHS, NewRHSKind); } }; Twine operator+(const Twine &LHS, const Twine &RHS) { return LHS.concat(RHS); } int main(int argc, char* argv[]) { Twine dt("A"); dt = dt + "B"; // First time this works since the temporary Child data is entirely copied (POD = deep copy) dt = dt + "C"; // Second time this doesn't work! dt's Child LHS (which contained the pointer to the data) is // substituted with a pointer to itself. No more data. // dt.str(); // Infinite recursion when exploring LHS and stack overflow return 0; }
Whether or not this is considered a bug, it can introduce subtle memory problems in an application and aside from the "Twine is inherently dangerous" line, there's no mention of it. The proposed fix attempts to avoid such a (likely) unintended use.
As for the "Rule of Three", I believe the issue only stands for already-initialized reuse of objects, thus: only for assignment operators.