Changeset View
Changeset View
Standalone View
Standalone View
head/devel/llvm60/files/clang/patch-stable12-r342281.diff
Property | Old Value | New Value |
---|---|---|
fbsd:nokeywords | null | yes \ No newline at end of property \ No newline at end of property |
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
r342281 | dim | 2018-12-20 19:28:53 +0100 (Thu, 20 Dec 2018) | 24 lines | |||||
Pull in r329671 from upstream clang trunk (by Akira Hatanaka): | |||||
[ExprConstant] Use an AST node and a version number as a key to | |||||
create an APValue and retrieve it from map Temporaries. | |||||
The version number is needed when a single AST node is visited | |||||
multiple times and is used to create APValues that are required to be | |||||
distinct from each other (for example, MaterializeTemporaryExprs in | |||||
default arguments and VarDecls in loops). | |||||
rdar://problem/36505742 | |||||
Differential Revision: https://reviews.llvm.org/D42776 | |||||
This should fix 'Assertion failed: (Result.isUninit() && "temporary | |||||
created multiple times"), function createTemporary' errors (if | |||||
assertions are enabled, otherwise the compiler internal state might go | |||||
bad), when building the graphics/rawtherapee port. | |||||
Direct commit to stable/11 and stable/12, since head already has clang | |||||
7.0.1, which includes this change. | |||||
PR: 234144 | |||||
Index: tools/clang/include/clang/AST/APValue.h | |||||
=================================================================== | |||||
--- tools/clang/include/clang/AST/APValue.h (revision 342280) | |||||
+++ tools/clang/include/clang/AST/APValue.h (revision 342281) | |||||
@@ -53,7 +53,58 @@ class APValue { | |||||
MemberPointer, | |||||
AddrLabelDiff | |||||
}; | |||||
- typedef llvm::PointerUnion<const ValueDecl *, const Expr *> LValueBase; | |||||
+ | |||||
+ class LValueBase { | |||||
+ public: | |||||
+ typedef llvm::PointerUnion<const ValueDecl *, const Expr *> PtrTy; | |||||
+ | |||||
+ LValueBase() : CallIndex(0), Version(0) {} | |||||
+ | |||||
+ template <class T> | |||||
+ LValueBase(T P, unsigned I = 0, unsigned V = 0) | |||||
+ : Ptr(P), CallIndex(I), Version(V) {} | |||||
+ | |||||
+ template <class T> | |||||
+ bool is() const { return Ptr.is<T>(); } | |||||
+ | |||||
+ template <class T> | |||||
+ T get() const { return Ptr.get<T>(); } | |||||
+ | |||||
+ template <class T> | |||||
+ T dyn_cast() const { return Ptr.dyn_cast<T>(); } | |||||
+ | |||||
+ void *getOpaqueValue() const; | |||||
+ | |||||
+ bool isNull() const; | |||||
+ | |||||
+ explicit operator bool () const; | |||||
+ | |||||
+ PtrTy getPointer() const { | |||||
+ return Ptr; | |||||
+ } | |||||
+ | |||||
+ unsigned getCallIndex() const { | |||||
+ return CallIndex; | |||||
+ } | |||||
+ | |||||
+ void setCallIndex(unsigned Index) { | |||||
+ CallIndex = Index; | |||||
+ } | |||||
+ | |||||
+ unsigned getVersion() const { | |||||
+ return Version; | |||||
+ } | |||||
+ | |||||
+ bool operator==(const LValueBase &Other) const { | |||||
+ return Ptr == Other.Ptr && CallIndex == Other.CallIndex && | |||||
+ Version == Other.Version; | |||||
+ } | |||||
+ | |||||
+ private: | |||||
+ PtrTy Ptr; | |||||
+ unsigned CallIndex, Version; | |||||
+ }; | |||||
+ | |||||
typedef llvm::PointerIntPair<const Decl *, 1, bool> BaseOrMemberType; | |||||
union LValuePathEntry { | |||||
/// BaseOrMember - The FieldDecl or CXXRecordDecl indicating the next item | |||||
@@ -135,15 +186,15 @@ class APValue { | |||||
} | |||||
APValue(const APValue &RHS); | |||||
APValue(APValue &&RHS) : Kind(Uninitialized) { swap(RHS); } | |||||
- APValue(LValueBase B, const CharUnits &O, NoLValuePath N, unsigned CallIndex, | |||||
+ APValue(LValueBase B, const CharUnits &O, NoLValuePath N, | |||||
bool IsNullPtr = false) | |||||
: Kind(Uninitialized) { | |||||
- MakeLValue(); setLValue(B, O, N, CallIndex, IsNullPtr); | |||||
+ MakeLValue(); setLValue(B, O, N, IsNullPtr); | |||||
} | |||||
APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path, | |||||
- bool OnePastTheEnd, unsigned CallIndex, bool IsNullPtr = false) | |||||
+ bool OnePastTheEnd, bool IsNullPtr = false) | |||||
: Kind(Uninitialized) { | |||||
- MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, CallIndex, IsNullPtr); | |||||
+ MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr); | |||||
} | |||||
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) { | |||||
MakeArray(InitElts, Size); | |||||
@@ -255,6 +306,7 @@ class APValue { | |||||
bool hasLValuePath() const; | |||||
ArrayRef<LValuePathEntry> getLValuePath() const; | |||||
unsigned getLValueCallIndex() const; | |||||
+ unsigned getLValueVersion() const; | |||||
bool isNullPointer() const; | |||||
APValue &getVectorElt(unsigned I) { | |||||
@@ -376,10 +428,10 @@ class APValue { | |||||
((ComplexAPFloat *)(char *)Data.buffer)->Imag = std::move(I); | |||||
} | |||||
void setLValue(LValueBase B, const CharUnits &O, NoLValuePath, | |||||
- unsigned CallIndex, bool IsNullPtr); | |||||
+ bool IsNullPtr); | |||||
void setLValue(LValueBase B, const CharUnits &O, | |||||
ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd, | |||||
- unsigned CallIndex, bool IsNullPtr); | |||||
+ bool IsNullPtr); | |||||
void setUnion(const FieldDecl *Field, const APValue &Value) { | |||||
assert(isUnion() && "Invalid accessor"); | |||||
((UnionData*)(char*)Data.buffer)->Field = Field; | |||||
@@ -451,4 +503,14 @@ class APValue { | |||||
} // end namespace clang. | |||||
+namespace llvm { | |||||
+template<> struct DenseMapInfo<clang::APValue::LValueBase> { | |||||
+ static clang::APValue::LValueBase getEmptyKey(); | |||||
+ static clang::APValue::LValueBase getTombstoneKey(); | |||||
+ static unsigned getHashValue(const clang::APValue::LValueBase &Base); | |||||
+ static bool isEqual(const clang::APValue::LValueBase &LHS, | |||||
+ const clang::APValue::LValueBase &RHS); | |||||
+}; | |||||
+} | |||||
+ | |||||
#endif | |||||
Index: tools/clang/lib/AST/APValue.cpp | |||||
=================================================================== | |||||
--- tools/clang/lib/AST/APValue.cpp (revision 342280) | |||||
+++ tools/clang/lib/AST/APValue.cpp (revision 342281) | |||||
@@ -23,14 +23,57 @@ using namespace clang; | |||||
namespace { | |||||
struct LVBase { | |||||
- llvm::PointerIntPair<APValue::LValueBase, 1, bool> BaseAndIsOnePastTheEnd; | |||||
+ APValue::LValueBase Base; | |||||
CharUnits Offset; | |||||
unsigned PathLength; | |||||
- unsigned CallIndex; | |||||
- bool IsNullPtr; | |||||
+ bool IsNullPtr : 1; | |||||
+ bool IsOnePastTheEnd : 1; | |||||
}; | |||||
} | |||||
+void *APValue::LValueBase::getOpaqueValue() const { | |||||
+ return Ptr.getOpaqueValue(); | |||||
+} | |||||
+ | |||||
+bool APValue::LValueBase::isNull() const { | |||||
+ return Ptr.isNull(); | |||||
+} | |||||
+ | |||||
+APValue::LValueBase::operator bool () const { | |||||
+ return static_cast<bool>(Ptr); | |||||
+} | |||||
+ | |||||
+clang::APValue::LValueBase | |||||
+llvm::DenseMapInfo<clang::APValue::LValueBase>::getEmptyKey() { | |||||
+ return clang::APValue::LValueBase( | |||||
+ DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getEmptyKey(), | |||||
+ DenseMapInfo<unsigned>::getEmptyKey(), | |||||
+ DenseMapInfo<unsigned>::getEmptyKey()); | |||||
+} | |||||
+ | |||||
+clang::APValue::LValueBase | |||||
+llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() { | |||||
+ return clang::APValue::LValueBase( | |||||
+ DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getTombstoneKey(), | |||||
+ DenseMapInfo<unsigned>::getTombstoneKey(), | |||||
+ DenseMapInfo<unsigned>::getTombstoneKey()); | |||||
+} | |||||
+ | |||||
+unsigned llvm::DenseMapInfo<clang::APValue::LValueBase>::getHashValue( | |||||
+ const clang::APValue::LValueBase &Base) { | |||||
+ llvm::FoldingSetNodeID ID; | |||||
+ ID.AddPointer(Base.getOpaqueValue()); | |||||
+ ID.AddInteger(Base.getCallIndex()); | |||||
+ ID.AddInteger(Base.getVersion()); | |||||
+ return ID.ComputeHash(); | |||||
+} | |||||
+ | |||||
+bool llvm::DenseMapInfo<clang::APValue::LValueBase>::isEqual( | |||||
+ const clang::APValue::LValueBase &LHS, | |||||
+ const clang::APValue::LValueBase &RHS) { | |||||
+ return LHS == RHS; | |||||
+} | |||||
+ | |||||
struct APValue::LV : LVBase { | |||||
static const unsigned InlinePathSpace = | |||||
(DataSize - sizeof(LVBase)) / sizeof(LValuePathEntry); | |||||
@@ -150,11 +193,10 @@ APValue::APValue(const APValue &RHS) : Kind(Uninit | |||||
MakeLValue(); | |||||
if (RHS.hasLValuePath()) | |||||
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), RHS.getLValuePath(), | |||||
- RHS.isLValueOnePastTheEnd(), RHS.getLValueCallIndex(), | |||||
- RHS.isNullPointer()); | |||||
+ RHS.isLValueOnePastTheEnd(), RHS.isNullPointer()); | |||||
else | |||||
setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath(), | |||||
- RHS.getLValueCallIndex(), RHS.isNullPointer()); | |||||
+ RHS.isNullPointer()); | |||||
break; | |||||
case Array: | |||||
MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize()); | |||||
@@ -552,12 +594,12 @@ std::string APValue::getAsString(ASTContext &Ctx, | |||||
const APValue::LValueBase APValue::getLValueBase() const { | |||||
assert(isLValue() && "Invalid accessor"); | |||||
- return ((const LV*)(const void*)Data.buffer)->BaseAndIsOnePastTheEnd.getPointer(); | |||||
+ return ((const LV*)(const void*)Data.buffer)->Base; | |||||
} | |||||
bool APValue::isLValueOnePastTheEnd() const { | |||||
assert(isLValue() && "Invalid accessor"); | |||||
- return ((const LV*)(const void*)Data.buffer)->BaseAndIsOnePastTheEnd.getInt(); | |||||
+ return ((const LV*)(const void*)Data.buffer)->IsOnePastTheEnd; | |||||
} | |||||
CharUnits &APValue::getLValueOffset() { | |||||
@@ -578,9 +620,14 @@ ArrayRef<APValue::LValuePathEntry> APValue::getLVa | |||||
unsigned APValue::getLValueCallIndex() const { | |||||
assert(isLValue() && "Invalid accessor"); | |||||
- return ((const LV*)(const char*)Data.buffer)->CallIndex; | |||||
+ return ((const LV*)(const char*)Data.buffer)->Base.getCallIndex(); | |||||
} | |||||
+unsigned APValue::getLValueVersion() const { | |||||
+ assert(isLValue() && "Invalid accessor"); | |||||
+ return ((const LV*)(const char*)Data.buffer)->Base.getVersion(); | |||||
+} | |||||
+ | |||||
bool APValue::isNullPointer() const { | |||||
assert(isLValue() && "Invalid usage"); | |||||
return ((const LV*)(const char*)Data.buffer)->IsNullPtr; | |||||
@@ -587,13 +634,12 @@ bool APValue::isNullPointer() const { | |||||
} | |||||
void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, | |||||
- unsigned CallIndex, bool IsNullPtr) { | |||||
+ bool IsNullPtr) { | |||||
assert(isLValue() && "Invalid accessor"); | |||||
LV &LVal = *((LV*)(char*)Data.buffer); | |||||
- LVal.BaseAndIsOnePastTheEnd.setPointer(B); | |||||
- LVal.BaseAndIsOnePastTheEnd.setInt(false); | |||||
+ LVal.Base = B; | |||||
+ LVal.IsOnePastTheEnd = false; | |||||
LVal.Offset = O; | |||||
- LVal.CallIndex = CallIndex; | |||||
LVal.resizePath((unsigned)-1); | |||||
LVal.IsNullPtr = IsNullPtr; | |||||
} | |||||
@@ -600,13 +646,12 @@ void APValue::setLValue(LValueBase B, const CharUn | |||||
void APValue::setLValue(LValueBase B, const CharUnits &O, | |||||
ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd, | |||||
- unsigned CallIndex, bool IsNullPtr) { | |||||
+ bool IsNullPtr) { | |||||
assert(isLValue() && "Invalid accessor"); | |||||
LV &LVal = *((LV*)(char*)Data.buffer); | |||||
- LVal.BaseAndIsOnePastTheEnd.setPointer(B); | |||||
- LVal.BaseAndIsOnePastTheEnd.setInt(IsOnePastTheEnd); | |||||
+ LVal.Base = B; | |||||
+ LVal.IsOnePastTheEnd = IsOnePastTheEnd; | |||||
LVal.Offset = O; | |||||
- LVal.CallIndex = CallIndex; | |||||
LVal.resizePath(Path.size()); | |||||
memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry)); | |||||
LVal.IsNullPtr = IsNullPtr; | |||||
Index: tools/clang/lib/AST/ExprConstant.cpp | |||||
=================================================================== | |||||
--- tools/clang/lib/AST/ExprConstant.cpp (revision 342280) | |||||
+++ tools/clang/lib/AST/ExprConstant.cpp (revision 342281) | |||||
@@ -446,8 +446,8 @@ namespace { | |||||
// Note that we intentionally use std::map here so that references to | |||||
// values are stable. | |||||
- typedef std::map<const void*, APValue> MapTy; | |||||
- typedef MapTy::const_iterator temp_iterator; | |||||
+ typedef std::pair<const void *, unsigned> MapKeyTy; | |||||
+ typedef std::map<MapKeyTy, APValue> MapTy; | |||||
/// Temporaries - Temporary lvalues materialized within this stack frame. | |||||
MapTy Temporaries; | |||||
@@ -457,6 +457,20 @@ namespace { | |||||
/// Index - The call index of this call. | |||||
unsigned Index; | |||||
+ /// The stack of integers for tracking version numbers for temporaries. | |||||
+ SmallVector<unsigned, 2> TempVersionStack = {1}; | |||||
+ unsigned CurTempVersion = TempVersionStack.back(); | |||||
+ | |||||
+ unsigned getTempVersion() const { return TempVersionStack.back(); } | |||||
+ | |||||
+ void pushTempVersion() { | |||||
+ TempVersionStack.push_back(++CurTempVersion); | |||||
+ } | |||||
+ | |||||
+ void popTempVersion() { | |||||
+ TempVersionStack.pop_back(); | |||||
+ } | |||||
+ | |||||
// FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact | |||||
// on the overall stack usage of deeply-recursing constexpr evaluataions. | |||||
// (We should cache this map rather than recomputing it repeatedly.) | |||||
@@ -473,10 +487,36 @@ namespace { | |||||
APValue *Arguments); | |||||
~CallStackFrame(); | |||||
- APValue *getTemporary(const void *Key) { | |||||
- MapTy::iterator I = Temporaries.find(Key); | |||||
- return I == Temporaries.end() ? nullptr : &I->second; | |||||
+ // Return the temporary for Key whose version number is Version. | |||||
+ APValue *getTemporary(const void *Key, unsigned Version) { | |||||
+ MapKeyTy KV(Key, Version); | |||||
+ auto LB = Temporaries.lower_bound(KV); | |||||
+ if (LB != Temporaries.end() && LB->first == KV) | |||||
+ return &LB->second; | |||||
+ // Pair (Key,Version) wasn't found in the map. Check that no elements | |||||
+ // in the map have 'Key' as their key. | |||||
+ assert((LB == Temporaries.end() || LB->first.first != Key) && | |||||
+ (LB == Temporaries.begin() || std::prev(LB)->first.first != Key) && | |||||
+ "Element with key 'Key' found in map"); | |||||
+ return nullptr; | |||||
} | |||||
+ | |||||
+ // Return the current temporary for Key in the map. | |||||
+ APValue *getCurrentTemporary(const void *Key) { | |||||
+ auto UB = Temporaries.upper_bound(MapKeyTy(Key, UINT_MAX)); | |||||
+ if (UB != Temporaries.begin() && std::prev(UB)->first.first == Key) | |||||
+ return &std::prev(UB)->second; | |||||
+ return nullptr; | |||||
+ } | |||||
+ | |||||
+ // Return the version number of the current temporary for Key. | |||||
+ unsigned getCurrentTemporaryVersion(const void *Key) const { | |||||
+ auto UB = Temporaries.upper_bound(MapKeyTy(Key, UINT_MAX)); | |||||
+ if (UB != Temporaries.begin() && std::prev(UB)->first.first == Key) | |||||
+ return std::prev(UB)->first.second; | |||||
+ return 0; | |||||
+ } | |||||
+ | |||||
APValue &createTemporary(const void *Key, bool IsLifetimeExtended); | |||||
}; | |||||
@@ -606,7 +646,8 @@ namespace { | |||||
/// EvaluatingObject - Pair of the AST node that an lvalue represents and | |||||
/// the call index that that lvalue was allocated in. | |||||
- typedef std::pair<APValue::LValueBase, unsigned> EvaluatingObject; | |||||
+ typedef std::pair<APValue::LValueBase, std::pair<unsigned, unsigned>> | |||||
+ EvaluatingObject; | |||||
/// EvaluatingConstructors - Set of objects that are currently being | |||||
/// constructed. | |||||
@@ -625,8 +666,10 @@ namespace { | |||||
} | |||||
}; | |||||
- bool isEvaluatingConstructor(APValue::LValueBase Decl, unsigned CallIndex) { | |||||
- return EvaluatingConstructors.count(EvaluatingObject(Decl, CallIndex)); | |||||
+ bool isEvaluatingConstructor(APValue::LValueBase Decl, unsigned CallIndex, | |||||
+ unsigned Version) { | |||||
+ return EvaluatingConstructors.count( | |||||
+ EvaluatingObject(Decl, {CallIndex, Version})); | |||||
} | |||||
/// The current array initialization index, if we're performing array | |||||
@@ -722,7 +765,7 @@ namespace { | |||||
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { | |||||
EvaluatingDecl = Base; | |||||
EvaluatingDeclValue = &Value; | |||||
- EvaluatingConstructors.insert({Base, 0}); | |||||
+ EvaluatingConstructors.insert({Base, {0, 0}}); | |||||
} | |||||
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } | |||||
@@ -1086,11 +1129,16 @@ namespace { | |||||
unsigned OldStackSize; | |||||
public: | |||||
ScopeRAII(EvalInfo &Info) | |||||
- : Info(Info), OldStackSize(Info.CleanupStack.size()) {} | |||||
+ : Info(Info), OldStackSize(Info.CleanupStack.size()) { | |||||
+ // Push a new temporary version. This is needed to distinguish between | |||||
+ // temporaries created in different iterations of a loop. | |||||
+ Info.CurrentCall->pushTempVersion(); | |||||
+ } | |||||
~ScopeRAII() { | |||||
// Body moved to a static method to encourage the compiler to inline away | |||||
// instances of this class. | |||||
cleanup(Info, OldStackSize); | |||||
+ Info.CurrentCall->popTempVersion(); | |||||
} | |||||
private: | |||||
static void cleanup(EvalInfo &Info, unsigned OldStackSize) { | |||||
@@ -1170,7 +1218,8 @@ CallStackFrame::~CallStackFrame() { | |||||
APValue &CallStackFrame::createTemporary(const void *Key, | |||||
bool IsLifetimeExtended) { | |||||
- APValue &Result = Temporaries[Key]; | |||||
+ unsigned Version = Info.CurrentCall->getTempVersion(); | |||||
+ APValue &Result = Temporaries[MapKeyTy(Key, Version)]; | |||||
assert(Result.isUninit() && "temporary created multiple times"); | |||||
Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended)); | |||||
return Result; | |||||
@@ -1262,27 +1311,27 @@ namespace { | |||||
struct LValue { | |||||
APValue::LValueBase Base; | |||||
CharUnits Offset; | |||||
- unsigned InvalidBase : 1; | |||||
- unsigned CallIndex : 31; | |||||
SubobjectDesignator Designator; | |||||
- bool IsNullPtr; | |||||
+ bool IsNullPtr : 1; | |||||
+ bool InvalidBase : 1; | |||||
const APValue::LValueBase getLValueBase() const { return Base; } | |||||
CharUnits &getLValueOffset() { return Offset; } | |||||
const CharUnits &getLValueOffset() const { return Offset; } | |||||
- unsigned getLValueCallIndex() const { return CallIndex; } | |||||
SubobjectDesignator &getLValueDesignator() { return Designator; } | |||||
const SubobjectDesignator &getLValueDesignator() const { return Designator;} | |||||
bool isNullPointer() const { return IsNullPtr;} | |||||
+ unsigned getLValueCallIndex() const { return Base.getCallIndex(); } | |||||
+ unsigned getLValueVersion() const { return Base.getVersion(); } | |||||
+ | |||||
void moveInto(APValue &V) const { | |||||
if (Designator.Invalid) | |||||
- V = APValue(Base, Offset, APValue::NoLValuePath(), CallIndex, | |||||
- IsNullPtr); | |||||
+ V = APValue(Base, Offset, APValue::NoLValuePath(), IsNullPtr); | |||||
else { | |||||
assert(!InvalidBase && "APValues can't handle invalid LValue bases"); | |||||
V = APValue(Base, Offset, Designator.Entries, | |||||
- Designator.IsOnePastTheEnd, CallIndex, IsNullPtr); | |||||
+ Designator.IsOnePastTheEnd, IsNullPtr); | |||||
} | |||||
} | |||||
void setFrom(ASTContext &Ctx, const APValue &V) { | |||||
@@ -1290,12 +1339,11 @@ namespace { | |||||
Base = V.getLValueBase(); | |||||
Offset = V.getLValueOffset(); | |||||
InvalidBase = false; | |||||
- CallIndex = V.getLValueCallIndex(); | |||||
Designator = SubobjectDesignator(Ctx, V); | |||||
IsNullPtr = V.isNullPointer(); | |||||
} | |||||
- void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false) { | |||||
+ void set(APValue::LValueBase B, bool BInvalid = false) { | |||||
#ifndef NDEBUG | |||||
// We only allow a few types of invalid bases. Enforce that here. | |||||
if (BInvalid) { | |||||
@@ -1308,7 +1356,6 @@ namespace { | |||||
Base = B; | |||||
Offset = CharUnits::fromQuantity(0); | |||||
InvalidBase = BInvalid; | |||||
- CallIndex = I; | |||||
Designator = SubobjectDesignator(getType(B)); | |||||
IsNullPtr = false; | |||||
} | |||||
@@ -1317,13 +1364,12 @@ namespace { | |||||
Base = (Expr *)nullptr; | |||||
Offset = CharUnits::fromQuantity(TargetVal); | |||||
InvalidBase = false; | |||||
- CallIndex = 0; | |||||
Designator = SubobjectDesignator(PointerTy->getPointeeType()); | |||||
IsNullPtr = true; | |||||
} | |||||
void setInvalid(APValue::LValueBase B, unsigned I = 0) { | |||||
- set(B, I, true); | |||||
+ set(B, true); | |||||
} | |||||
// Check that this LValue is not based on a null pointer. If it is, produce | |||||
@@ -1525,6 +1571,15 @@ static bool EvaluateAsRValue(EvalInfo &Info, const | |||||
// Misc utilities | |||||
//===----------------------------------------------------------------------===// | |||||
+/// A helper function to create a temporary and set an LValue. | |||||
+template <class KeyTy> | |||||
+static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended, | |||||
+ LValue &LV, CallStackFrame &Frame) { | |||||
+ LV.set({Key, Frame.Info.CurrentCall->Index, | |||||
+ Frame.Info.CurrentCall->getTempVersion()}); | |||||
+ return Frame.createTemporary(Key, IsLifetimeExtended); | |||||
+} | |||||
+ | |||||
/// Negate an APSInt in place, converting it to a signed form if necessary, and | |||||
/// preserving its value (by extending by up to one bit as needed). | |||||
static void negateAsSigned(APSInt &Int) { | |||||
@@ -1854,7 +1909,7 @@ static const ValueDecl *GetLValueBaseDecl(const LV | |||||
} | |||||
static bool IsLiteralLValue(const LValue &Value) { | |||||
- if (Value.CallIndex) | |||||
+ if (Value.getLValueCallIndex()) | |||||
return false; | |||||
const Expr *E = Value.Base.dyn_cast<const Expr*>(); | |||||
return E && !isa<MaterializeTemporaryExpr>(E); | |||||
@@ -2404,7 +2459,7 @@ static bool handleLValueToRValueConversion(EvalInf | |||||
/// \param Result Filled in with a pointer to the value of the variable. | |||||
static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, | |||||
const VarDecl *VD, CallStackFrame *Frame, | |||||
- APValue *&Result) { | |||||
+ APValue *&Result, const LValue *LVal) { | |||||
// If this is a parameter to an active constexpr function call, perform | |||||
// argument substitution. | |||||
@@ -2423,7 +2478,8 @@ static bool evaluateVarDeclInit(EvalInfo &Info, co | |||||
// If this is a local variable, dig out its value. | |||||
if (Frame) { | |||||
- Result = Frame->getTemporary(VD); | |||||
+ Result = LVal ? Frame->getTemporary(VD, LVal->getLValueVersion()) | |||||
+ : Frame->getCurrentTemporary(VD); | |||||
if (!Result) { | |||||
// Assume variables referenced within a lambda's call operator that were | |||||
// not declared within the call operator are captures and during checking | |||||
@@ -3000,8 +3056,8 @@ static CompleteObject findCompleteObject(EvalInfo | |||||
} | |||||
CallStackFrame *Frame = nullptr; | |||||
- if (LVal.CallIndex) { | |||||
- Frame = Info.getCallFrame(LVal.CallIndex); | |||||
+ if (LVal.getLValueCallIndex()) { | |||||
+ Frame = Info.getCallFrame(LVal.getLValueCallIndex()); | |||||
if (!Frame) { | |||||
Info.FFDiag(E, diag::note_constexpr_lifetime_ended, 1) | |||||
<< AK << LVal.Base.is<const ValueDecl*>(); | |||||
@@ -3113,7 +3169,7 @@ static CompleteObject findCompleteObject(EvalInfo | |||||
} | |||||
} | |||||
- if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal)) | |||||
+ if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) | |||||
return CompleteObject(); | |||||
} else { | |||||
const Expr *Base = LVal.Base.dyn_cast<const Expr*>(); | |||||
@@ -3155,7 +3211,7 @@ static CompleteObject findCompleteObject(EvalInfo | |||||
return CompleteObject(); | |||||
} | |||||
} else { | |||||
- BaseVal = Frame->getTemporary(Base); | |||||
+ BaseVal = Frame->getTemporary(Base, LVal.Base.getVersion()); | |||||
assert(BaseVal && "missing value for temporary"); | |||||
} | |||||
@@ -3175,7 +3231,9 @@ static CompleteObject findCompleteObject(EvalInfo | |||||
// During the construction of an object, it is not yet 'const'. | |||||
// FIXME: This doesn't do quite the right thing for const subobjects of the | |||||
// object under construction. | |||||
- if (Info.isEvaluatingConstructor(LVal.getLValueBase(), LVal.CallIndex)) { | |||||
+ if (Info.isEvaluatingConstructor(LVal.getLValueBase(), | |||||
+ LVal.getLValueCallIndex(), | |||||
+ LVal.getLValueVersion())) { | |||||
BaseType = Info.Ctx.getCanonicalType(BaseType); | |||||
BaseType.removeLocalConst(); | |||||
} | |||||
@@ -3212,7 +3270,7 @@ static bool handleLValueToRValueConversion(EvalInf | |||||
// Check for special cases where there is no existing APValue to look at. | |||||
const Expr *Base = LVal.Base.dyn_cast<const Expr*>(); | |||||
- if (Base && !LVal.CallIndex && !Type.isVolatileQualified()) { | |||||
+ if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) { | |||||
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) { | |||||
// In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the | |||||
// initializer until now for such expressions. Such an expression can't be | |||||
@@ -3715,8 +3773,7 @@ static bool EvaluateVarDecl(EvalInfo &Info, const | |||||
return true; | |||||
LValue Result; | |||||
- Result.set(VD, Info.CurrentCall->Index); | |||||
- APValue &Val = Info.CurrentCall->createTemporary(VD, true); | |||||
+ APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall); | |||||
const Expr *InitE = VD->getInit(); | |||||
if (!InitE) { | |||||
@@ -3772,6 +3829,19 @@ struct StmtResult { | |||||
/// The location containing the result, if any (used to support RVO). | |||||
const LValue *Slot; | |||||
}; | |||||
+ | |||||
+struct TempVersionRAII { | |||||
+ CallStackFrame &Frame; | |||||
+ | |||||
+ TempVersionRAII(CallStackFrame &Frame) : Frame(Frame) { | |||||
+ Frame.pushTempVersion(); | |||||
+ } | |||||
+ | |||||
+ ~TempVersionRAII() { | |||||
+ Frame.popTempVersion(); | |||||
+ } | |||||
+}; | |||||
+ | |||||
} | |||||
static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, | |||||
@@ -4329,7 +4399,8 @@ static bool HandleConstructorCall(const Expr *E, c | |||||
} | |||||
EvalInfo::EvaluatingConstructorRAII EvalObj( | |||||
- Info, {This.getLValueBase(), This.CallIndex}); | |||||
+ Info, {This.getLValueBase(), | |||||
+ {This.getLValueCallIndex(), This.getLValueVersion()}}); | |||||
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues); | |||||
// FIXME: Creating an APValue just to hold a nonexistent return value is | |||||
@@ -4578,9 +4649,12 @@ class ExprEvaluatorBase | |||||
{ return StmtVisitorTy::Visit(E->getResultExpr()); } | |||||
bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) | |||||
{ return StmtVisitorTy::Visit(E->getReplacement()); } | |||||
- bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) | |||||
- { return StmtVisitorTy::Visit(E->getExpr()); } | |||||
+ bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { | |||||
+ TempVersionRAII RAII(*Info.CurrentCall); | |||||
+ return StmtVisitorTy::Visit(E->getExpr()); | |||||
+ } | |||||
bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { | |||||
+ TempVersionRAII RAII(*Info.CurrentCall); | |||||
// The initializer may not have been parsed yet, or might be erroneous. | |||||
if (!E->getExpr()) | |||||
return Error(E); | |||||
@@ -4658,7 +4732,7 @@ class ExprEvaluatorBase | |||||
} | |||||
bool VisitOpaqueValueExpr(const OpaqueValueExpr *E) { | |||||
- if (APValue *Value = Info.CurrentCall->getTemporary(E)) | |||||
+ if (APValue *Value = Info.CurrentCall->getCurrentTemporary(E)) | |||||
return DerivedSuccess(*Value, E); | |||||
const Expr *Source = E->getSourceExpr(); | |||||
@@ -5216,7 +5290,8 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr | |||||
if (!VD->getType()->isReferenceType()) { | |||||
if (Frame) { | |||||
- Result.set(VD, Frame->Index); | |||||
+ Result.set({VD, Frame->Index, | |||||
+ Info.CurrentCall->getCurrentTemporaryVersion(VD)}); | |||||
return true; | |||||
} | |||||
return Success(VD); | |||||
@@ -5223,7 +5298,7 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr | |||||
} | |||||
APValue *V; | |||||
- if (!evaluateVarDeclInit(Info, E, VD, Frame, V)) | |||||
+ if (!evaluateVarDeclInit(Info, E, VD, Frame, V, nullptr)) | |||||
return false; | |||||
if (V->isUninit()) { | |||||
if (!Info.checkingPotentialConstantExpression()) | |||||
@@ -5255,9 +5330,8 @@ bool LValueExprEvaluator::VisitMaterializeTemporar | |||||
*Value = APValue(); | |||||
Result.set(E); | |||||
} else { | |||||
- Value = &Info.CurrentCall-> | |||||
- createTemporary(E, E->getStorageDuration() == SD_Automatic); | |||||
- Result.set(E, Info.CurrentCall->Index); | |||||
+ Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result, | |||||
+ *Info.CurrentCall); | |||||
} | |||||
QualType Type = Inner->getType(); | |||||
@@ -5736,7 +5810,6 @@ bool PointerExprEvaluator::VisitCastExpr(const Cas | |||||
Result.Base = (Expr*)nullptr; | |||||
Result.InvalidBase = false; | |||||
Result.Offset = CharUnits::fromQuantity(N); | |||||
- Result.CallIndex = 0; | |||||
Result.Designator.setInvalid(); | |||||
Result.IsNullPtr = false; | |||||
return true; | |||||
@@ -5752,9 +5825,9 @@ bool PointerExprEvaluator::VisitCastExpr(const Cas | |||||
if (!evaluateLValue(SubExpr, Result)) | |||||
return false; | |||||
} else { | |||||
- Result.set(SubExpr, Info.CurrentCall->Index); | |||||
- if (!EvaluateInPlace(Info.CurrentCall->createTemporary(SubExpr, false), | |||||
- Info, Result, SubExpr)) | |||||
+ APValue &Value = createTemporary(SubExpr, false, Result, | |||||
+ *Info.CurrentCall); | |||||
+ if (!EvaluateInPlace(Value, Info, Result, SubExpr)) | |||||
return false; | |||||
} | |||||
// The result is a pointer to the first element of the array. | |||||
@@ -6520,9 +6593,8 @@ class TemporaryExprEvaluator | |||||
/// Visit an expression which constructs the value of this temporary. | |||||
bool VisitConstructExpr(const Expr *E) { | |||||
- Result.set(E, Info.CurrentCall->Index); | |||||
- return EvaluateInPlace(Info.CurrentCall->createTemporary(E, false), | |||||
- Info, Result, E); | |||||
+ APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall); | |||||
+ return EvaluateInPlace(Value, Info, Result, E); | |||||
} | |||||
bool VisitCastExpr(const CastExpr *E) { | |||||
@@ -8007,7 +8079,8 @@ static bool HasSameBase(const LValue &A, const LVa | |||||
} | |||||
return IsGlobalLValue(A.getLValueBase()) || | |||||
- A.getLValueCallIndex() == B.getLValueCallIndex(); | |||||
+ (A.getLValueCallIndex() == B.getLValueCallIndex() && | |||||
+ A.getLValueVersion() == B.getLValueVersion()); | |||||
} | |||||
/// \brief Determine whether this is a pointer past the end of the complete | |||||
@@ -9941,15 +10014,13 @@ static bool Evaluate(APValue &Result, EvalInfo &In | |||||
return true; | |||||
} else if (T->isArrayType()) { | |||||
LValue LV; | |||||
- LV.set(E, Info.CurrentCall->Index); | |||||
- APValue &Value = Info.CurrentCall->createTemporary(E, false); | |||||
+ APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); | |||||
if (!EvaluateArray(E, LV, Value, Info)) | |||||
return false; | |||||
Result = Value; | |||||
} else if (T->isRecordType()) { | |||||
LValue LV; | |||||
- LV.set(E, Info.CurrentCall->Index); | |||||
- APValue &Value = Info.CurrentCall->createTemporary(E, false); | |||||
+ APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); | |||||
if (!EvaluateRecord(E, LV, Value, Info)) | |||||
return false; | |||||
Result = Value; | |||||
@@ -9963,8 +10034,7 @@ static bool Evaluate(APValue &Result, EvalInfo &In | |||||
QualType Unqual = T.getAtomicUnqualifiedType(); | |||||
if (Unqual->isArrayType() || Unqual->isRecordType()) { | |||||
LValue LV; | |||||
- LV.set(E, Info.CurrentCall->Index); | |||||
- APValue &Value = Info.CurrentCall->createTemporary(E, false); | |||||
+ APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); | |||||
if (!EvaluateAtomic(E, &LV, Value, Info)) | |||||
return false; | |||||
} else { | |||||
@@ -10786,7 +10856,7 @@ bool Expr::isPotentialConstantExpr(const FunctionD | |||||
// is a temporary being used as the 'this' pointer. | |||||
LValue This; | |||||
ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy); | |||||
- This.set(&VIE, Info.CurrentCall->Index); | |||||
+ This.set({&VIE, Info.CurrentCall->Index}); | |||||
ArrayRef<const Expr*> Args; | |||||
Index: tools/clang/test/SemaCXX/constant-expression-cxx1y.cpp | |||||
=================================================================== | |||||
--- tools/clang/test/SemaCXX/constant-expression-cxx1y.cpp (revision 342280) | |||||
+++ tools/clang/test/SemaCXX/constant-expression-cxx1y.cpp (revision 342281) | |||||
@@ -852,7 +852,6 @@ namespace Lifetime { | |||||
static_assert(h(2) == 0, ""); // expected-error {{constant expression}} expected-note {{in call}} | |||||
static_assert(h(3) == 0, ""); // expected-error {{constant expression}} expected-note {{in call}} | |||||
- // FIXME: This function should be treated as non-constant. | |||||
constexpr void lifetime_versus_loops() { | |||||
int *p = 0; | |||||
for (int i = 0; i != 2; ++i) { | |||||
@@ -862,10 +861,10 @@ namespace Lifetime { | |||||
if (i) | |||||
// This modifies the 'n' from the previous iteration of the loop outside | |||||
// its lifetime. | |||||
- ++*q; | |||||
+ ++*q; // expected-note {{increment of object outside its lifetime}} | |||||
} | |||||
} | |||||
- static_assert((lifetime_versus_loops(), true), ""); | |||||
+ static_assert((lifetime_versus_loops(), true), ""); // expected-error {{constant expression}} expected-note {{in call}} | |||||
} | |||||
namespace Bitfields { | |||||
Index: tools/clang/test/SemaCXX/constexpr-default-arg.cpp | |||||
=================================================================== | |||||
--- tools/clang/test/SemaCXX/constexpr-default-arg.cpp (nonexistent) | |||||
+++ tools/clang/test/SemaCXX/constexpr-default-arg.cpp (revision 342281) | |||||
@@ -0,0 +1,38 @@ | |||||
+// RUN: %clang_cc1 -std=c++1y -S -o - -emit-llvm -verify %s | |||||
+ | |||||
+namespace default_arg_temporary { | |||||
+ | |||||
+constexpr bool equals(const float& arg = 1.0f) { | |||||
+ return arg == 1.0f; | |||||
+} | |||||
+ | |||||
+constexpr const int &x(const int &p = 0) { | |||||
+ return p; | |||||
+} | |||||
+ | |||||
+struct S { | |||||
+ constexpr S(const int &a = 0) {} | |||||
+}; | |||||
+ | |||||
+void test_default_arg2() { | |||||
+ // This piece of code used to cause an assertion failure in | |||||
+ // CallStackFrame::createTemporary because the same MTE is used to initilize | |||||
+ // both elements of the array (see PR33140). | |||||
+ constexpr S s[2] = {}; | |||||
+ | |||||
+ // This piece of code used to cause an assertion failure in | |||||
+ // CallStackFrame::createTemporary because multiple CXXDefaultArgExpr share | |||||
+ // the same MTE (see PR33140). | |||||
+ static_assert(equals() && equals(), ""); | |||||
+ | |||||
+ // Test that constant expression evaluation produces distinct lvalues for | |||||
+ // each call. | |||||
+ static_assert(&x() != &x(), ""); | |||||
+} | |||||
+ | |||||
+// Check that multiple CXXDefaultInitExprs don't cause an assertion failure. | |||||
+struct A { int &&r = 0; }; // expected-warning {{binding reference member}} // expected-note {{reference member declared here}} | |||||
+struct B { A x, y; }; | |||||
+B b = {}; | |||||
+ | |||||
+} |