Refactor: split Uninitialized state on APValue into an "Absent" state

representing no such object, and an "Indeterminate" state representing
an uninitialized object. The latter is not yet used, but soon will be.

llvm-svn: 361328
This commit is contained in:
Richard Smith 2019-05-21 23:15:18 +00:00
parent a49496fb2a
commit e637cbe4e4
9 changed files with 83 additions and 61 deletions

View File

@ -78,7 +78,10 @@ class APValue {
typedef llvm::APFloat APFloat;
public:
enum ValueKind {
Uninitialized,
/// There is no such object (it's outside its lifetime).
None,
/// This object has an indeterminate value (C++ [basic.indet]).
Indeterminate,
Int,
Float,
FixedPoint,
@ -231,58 +234,59 @@ private:
DataType Data;
public:
APValue() : Kind(Uninitialized) {}
explicit APValue(APSInt I) : Kind(Uninitialized) {
APValue() : Kind(None) {}
explicit APValue(APSInt I) : Kind(None) {
MakeInt(); setInt(std::move(I));
}
explicit APValue(APFloat F) : Kind(Uninitialized) {
explicit APValue(APFloat F) : Kind(None) {
MakeFloat(); setFloat(std::move(F));
}
explicit APValue(APFixedPoint FX) : Kind(Uninitialized) {
explicit APValue(APFixedPoint FX) : Kind(None) {
MakeFixedPoint(std::move(FX));
}
explicit APValue(const APValue *E, unsigned N) : Kind(Uninitialized) {
explicit APValue(const APValue *E, unsigned N) : Kind(None) {
MakeVector(); setVector(E, N);
}
APValue(APSInt R, APSInt I) : Kind(Uninitialized) {
APValue(APSInt R, APSInt I) : Kind(None) {
MakeComplexInt(); setComplexInt(std::move(R), std::move(I));
}
APValue(APFloat R, APFloat I) : Kind(Uninitialized) {
APValue(APFloat R, APFloat I) : Kind(None) {
MakeComplexFloat(); setComplexFloat(std::move(R), std::move(I));
}
APValue(const APValue &RHS);
APValue(APValue &&RHS) : Kind(Uninitialized) { swap(RHS); }
APValue(APValue &&RHS) : Kind(None) { swap(RHS); }
APValue(LValueBase B, const CharUnits &O, NoLValuePath N,
bool IsNullPtr = false)
: Kind(Uninitialized) {
: Kind(None) {
MakeLValue(); setLValue(B, O, N, IsNullPtr);
}
APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
bool OnePastTheEnd, bool IsNullPtr = false)
: Kind(Uninitialized) {
: Kind(None) {
MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr);
}
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) {
APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) {
MakeArray(InitElts, Size);
}
APValue(UninitStruct, unsigned B, unsigned M) : Kind(Uninitialized) {
APValue(UninitStruct, unsigned B, unsigned M) : Kind(None) {
MakeStruct(B, M);
}
explicit APValue(const FieldDecl *D, const APValue &V = APValue())
: Kind(Uninitialized) {
: Kind(None) {
MakeUnion(); setUnion(D, V);
}
APValue(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path) : Kind(Uninitialized) {
ArrayRef<const CXXRecordDecl*> Path) : Kind(None) {
MakeMemberPointer(Member, IsDerivedMember, Path);
}
APValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr)
: Kind(Uninitialized) {
: Kind(None) {
MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr);
}
~APValue() {
MakeUninit();
if (Kind != None && Kind != Indeterminate)
DestroyDataAndMakeUninit();
}
/// Returns whether the object performed allocations.
@ -296,7 +300,11 @@ public:
void swap(APValue &RHS);
ValueKind getKind() const { return Kind; }
bool isUninit() const { return Kind == Uninitialized; }
bool isAbsent() const { return Kind == None; }
bool isIndeterminate() const { return Kind == Indeterminate; }
bool hasValue() const { return Kind != None && Kind != Indeterminate; }
bool isInt() const { return Kind == Int; }
bool isFloat() const { return Kind == Float; }
bool isFixedPoint() const { return Kind == FixedPoint; }
@ -536,56 +544,52 @@ public:
private:
void DestroyDataAndMakeUninit();
void MakeUninit() {
if (Kind != Uninitialized)
DestroyDataAndMakeUninit();
}
void MakeInt() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)Data.buffer) APSInt(1);
Kind = Int;
}
void MakeFloat() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) APFloat(0.0);
Kind = Float;
}
void MakeFixedPoint(APFixedPoint &&FX) {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void *)(char *)Data.buffer) APFixedPoint(std::move(FX));
Kind = FixedPoint;
}
void MakeVector() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) Vec();
Kind = Vector;
}
void MakeComplexInt() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) ComplexAPSInt();
Kind = ComplexInt;
}
void MakeComplexFloat() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) ComplexAPFloat();
Kind = ComplexFloat;
}
void MakeLValue();
void MakeArray(unsigned InitElts, unsigned Size);
void MakeStruct(unsigned B, unsigned M) {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) StructData(B, M);
Kind = Struct;
}
void MakeUnion() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) UnionData();
Kind = Union;
}
void MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path);
void MakeAddrLabelDiff() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) AddrLabelDiffData();
Kind = AddrLabelDiff;
}

View File

@ -115,7 +115,8 @@ def note_constexpr_lifetime_ended : Note<
def note_constexpr_access_uninit : Note<
"%select{read of|assignment to|increment of|decrement of|member call on|"
"dynamic_cast of|typeid applied to}0 "
"object outside its lifetime is not allowed in a constant expression">;
"%select{object outside its lifetime|uninitialized object}1 "
"is not allowed in a constant expression">;
def note_constexpr_use_uninit_reference : Note<
"use of reference outside its lifetime "
"is not allowed in a constant expression">;

View File

@ -219,9 +219,11 @@ APValue::UnionData::~UnionData () {
delete Value;
}
APValue::APValue(const APValue &RHS) : Kind(Uninitialized) {
APValue::APValue(const APValue &RHS) : Kind(None) {
switch (RHS.getKind()) {
case Uninitialized:
case None:
case Indeterminate:
Kind = RHS.getKind();
break;
case Int:
MakeInt();
@ -313,12 +315,13 @@ void APValue::DestroyDataAndMakeUninit() {
((MemberPointerData*)(char*)Data.buffer)->~MemberPointerData();
else if (Kind == AddrLabelDiff)
((AddrLabelDiffData*)(char*)Data.buffer)->~AddrLabelDiffData();
Kind = Uninitialized;
Kind = None;
}
bool APValue::needsCleanup() const {
switch (getKind()) {
case Uninitialized:
case None:
case Indeterminate:
case AddrLabelDiff:
return false;
case Struct:
@ -376,8 +379,11 @@ static double GetApproxValue(const llvm::APFloat &F) {
void APValue::dump(raw_ostream &OS) const {
switch (getKind()) {
case Uninitialized:
OS << "Uninitialized";
case None:
OS << "None";
return;
case Indeterminate:
OS << "Indeterminate";
return;
case Int:
OS << "Int: " << getInt();
@ -452,7 +458,10 @@ void APValue::dump(raw_ostream &OS) const {
void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
switch (getKind()) {
case APValue::Uninitialized:
case APValue::None:
Out << "<out of lifetime>";
return;
case APValue::Indeterminate:
Out << "<uninitialized>";
return;
case APValue::Int:
@ -781,21 +790,21 @@ ArrayRef<const CXXRecordDecl*> APValue::getMemberPointerPath() const {
}
void APValue::MakeLValue() {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
static_assert(sizeof(LV) <= DataSize, "LV too big");
new ((void*)(char*)Data.buffer) LV();
Kind = LValue;
}
void APValue::MakeArray(unsigned InitElts, unsigned Size) {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
new ((void*)(char*)Data.buffer) Arr(InitElts, Size);
Kind = Array;
}
void APValue::MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
ArrayRef<const CXXRecordDecl*> Path) {
assert(isUninit() && "Bad state change");
assert(isAbsent() && "Bad state change");
MemberPointerData *MPD = new ((void*)(char*)Data.buffer) MemberPointerData;
Kind = MemberPointer;
MPD->MemberAndIsDerivedMember.setPointer(Member);

View File

@ -2302,7 +2302,7 @@ APValue *VarDecl::evaluateValue(
// first time it is evaluated. FIXME: The notes won't always be emitted the
// first time we try evaluation, so might not be produced at all.
if (Eval->WasEvaluated)
return Eval->Evaluated.isUninit() ? nullptr : &Eval->Evaluated;
return Eval->Evaluated.isAbsent() ? nullptr : &Eval->Evaluated;
const auto *Init = cast<Expr>(Eval->Value);
assert(!Init->isValueDependent());

View File

@ -1291,7 +1291,7 @@ APValue &CallStackFrame::createTemporary(const void *Key,
bool IsLifetimeExtended) {
unsigned Version = Info.CurrentCall->getTempVersion();
APValue &Result = Temporaries[MapKeyTy(Key, Version)];
assert(Result.isUninit() && "temporary created multiple times");
assert(Result.isAbsent() && "temporary created multiple times");
Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended));
return Result;
}
@ -2025,7 +2025,7 @@ static bool
CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
const APValue &Value,
Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) {
if (Value.isUninit()) {
if (!Value.hasValue()) {
Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized)
<< true << Type;
return false;
@ -2108,7 +2108,8 @@ static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) {
static bool HandleConversionToBool(const APValue &Val, bool &Result) {
switch (Val.getKind()) {
case APValue::Uninitialized:
case APValue::None:
case APValue::Indeterminate:
return false;
case APValue::Int:
Result = Val.getInt().getBoolValue();
@ -2971,10 +2972,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
// Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
if (O->isUninit()) {
if (!O->hasValue()) {
if (!Info.checkingPotentialConstantExpression())
Info.FFDiag(E, diag::note_constexpr_access_uninit)
<< handler.AccessKind;
<< handler.AccessKind << O->isIndeterminate();
return handler.failed();
}
@ -5040,7 +5041,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
}
// Reserve space for the struct members.
if (!RD->isUnion() && Result.isUninit())
if (!RD->isUnion() && !Result.hasValue())
Result = APValue(APValue::UninitStruct(), RD->getNumBases(),
std::distance(RD->field_begin(), RD->field_end()));
@ -5097,7 +5098,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
// subobject other than the first.
// FIXME: In this case, the values of the other subobjects are
// specified, since zero-initialization sets all padding bits to zero.
if (Value->isUninit() ||
if (!Value->hasValue() ||
(Value->isUnion() && Value->getUnionField() != FD)) {
if (CD->isUnion())
*Value = APValue(FD);
@ -5953,7 +5954,9 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
APValue *V;
if (!evaluateVarDeclInit(Info, E, VD, Frame, V, nullptr))
return false;
if (V->isUninit()) {
if (!V->hasValue()) {
// FIXME: Is it possible for V to be indeterminate here? If so, we should
// adjust the diagnostic to say that.
if (!Info.checkingPotentialConstantExpression())
Info.FFDiag(E, diag::note_constexpr_use_uninit_reference);
return false;
@ -7217,7 +7220,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr);
}
if (Result.isUninit())
if (!Result.hasValue())
Result = APValue(APValue::UninitStruct(), CXXRD ? CXXRD->getNumBases() : 0,
std::distance(RD->field_begin(), RD->field_end()));
unsigned ElementNo = 0;
@ -7294,7 +7297,7 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
bool ZeroInit = E->requiresZeroInitialization();
if (CheckTrivialDefaultConstructor(Info, E->getExprLoc(), FD, ZeroInit)) {
// If we've already performed zero-initialization, we're already done.
if (!Result.isUninit())
if (Result.hasValue())
return true;
// We can get here in two different ways:
@ -7794,7 +7797,7 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
// If the array was previously zero-initialized, preserve the
// zero-initialized values.
if (!Filler.isUninit()) {
if (Filler.hasValue()) {
for (unsigned I = 0, E = Result.getArrayInitializedElts(); I != E; ++I)
Result.getArrayInitializedElt(I) = Filler;
if (Result.hasArrayFiller())
@ -7863,7 +7866,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
const LValue &Subobject,
APValue *Value,
QualType Type) {
bool HadZeroInit = !Value->isUninit();
bool HadZeroInit = Value->hasValue();
if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(Type)) {
unsigned N = CAT->getSize().getZExtValue();
@ -8427,7 +8430,7 @@ static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) {
return EvaluateBuiltinConstantPForLValue(V);
// Otherwise, any constant value is good enough.
return V.getKind() != APValue::Uninitialized;
return V.hasValue();
}
// Anything else isn't considered to be sufficiently constant.

View File

@ -1860,8 +1860,10 @@ ConstantLValueEmitter::VisitMaterializeTemporaryExpr(
llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value,
QualType DestType) {
switch (Value.getKind()) {
case APValue::Uninitialized:
llvm_unreachable("Constant expressions should be initialized.");
case APValue::None:
case APValue::Indeterminate:
// Out-of-lifetime and indeterminate values can be modeled as 'undef'.
return llvm::UndefValue::get(CGM.getTypes().ConvertType(DestType));
case APValue::LValue:
return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
case APValue::Int:

View File

@ -1990,7 +1990,7 @@ Address CodeGenFunction::EmitMSVAListRef(const Expr *E) {
void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E,
const APValue &Init) {
assert(!Init.isUninit() && "Invalid DeclRefExpr initializer!");
assert(Init.hasValue() && "Invalid DeclRefExpr initializer!");
if (CGDebugInfo *Dbg = getDebugInfo())
if (CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo)
Dbg->EmitGlobalVariable(E->getDecl(), Init);

View File

@ -4886,7 +4886,7 @@ ConstantAddress CodeGenModule::GetAddrOfGlobalTemporary(
// evaluating the initializer if the surrounding constant expression
// modifies the temporary.
Value = getContext().getMaterializedTemporaryValue(E, false);
if (Value && Value->isUninit())
if (Value && Value->isAbsent())
Value = nullptr;
}

View File

@ -6390,10 +6390,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// Convert the APValue to a TemplateArgument.
switch (Value.getKind()) {
case APValue::Uninitialized:
case APValue::None:
assert(ParamType->isNullPtrType());
Converted = TemplateArgument(CanonParamType, /*isNullPtr*/true);
break;
case APValue::Indeterminate:
llvm_unreachable("result of constant evaluation should be initialized");
break;
case APValue::Int:
assert(ParamType->isIntegralOrEnumerationType());
Converted = TemplateArgument(Context, Value.getInt(), CanonParamType);