diff options
Diffstat (limited to 'clang/lib/AST')
| -rw-r--r-- | clang/lib/AST/APValue.cpp | 34 | ||||
| -rw-r--r-- | clang/lib/AST/ExprCXX.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 489 |
3 files changed, 474 insertions, 55 deletions
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 1993bba9bd1..b44ab9837b3 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -42,6 +42,14 @@ APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V) APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) : Ptr(P), Local{I, V} {} +APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, + QualType Type) { + LValueBase Base; + Base.Ptr = LV; + Base.DynamicAllocType = Type.getAsOpaquePtr(); + return Base; +} + APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, QualType TypeInfo) { LValueBase Base; @@ -51,11 +59,12 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, } unsigned APValue::LValueBase::getCallIndex() const { - return is<TypeInfoLValue>() ? 0 : Local.CallIndex; + return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 0 + : Local.CallIndex; } unsigned APValue::LValueBase::getVersion() const { - return is<TypeInfoLValue>() ? 0 : Local.Version; + return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 0 : Local.Version; } QualType APValue::LValueBase::getTypeInfoType() const { @@ -63,6 +72,11 @@ QualType APValue::LValueBase::getTypeInfoType() const { return QualType::getFromOpaquePtr(TypeInfoType); } +QualType APValue::LValueBase::getDynamicAllocType() const { + assert(is<DynamicAllocLValue>() && "not a dynamic allocation lvalue"); + return QualType::getFromOpaquePtr(DynamicAllocType); +} + namespace clang { bool operator==(const APValue::LValueBase &LHS, const APValue::LValueBase &RHS) { @@ -111,7 +125,7 @@ llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { - if (Base.is<TypeInfoLValue>()) + if (Base.is<TypeInfoLValue>() || Base.is<DynamicAllocLValue>()) return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); @@ -528,13 +542,18 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, S = CharUnits::One(); } Out << '&'; - } else if (!IsReference) + } else if (!IsReference) { Out << '&'; + } if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) Out << *VD; else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) { TI.print(Out, Ctx.getPrintingPolicy()); + } else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; } else { assert(Base.get<const Expr *>() != nullptr && "Expecting non-null Expr"); @@ -563,10 +582,17 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, } else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) { TI.print(Out, Ctx.getPrintingPolicy()); ElemTy = Base.getTypeInfoType(); + } else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; + ElemTy = Base.getDynamicAllocType(); } else { const Expr *E = Base.get<const Expr*>(); assert(E != nullptr && "Expecting non-null Expr"); E->printPretty(Out, nullptr, Ctx.getPrintingPolicy()); + // FIXME: This is wrong if E is a MaterializeTemporaryExpr with an lvalue + // adjustment. ElemTy = E->getType(); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index c5f86a4cc12..30c28314d5b 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -124,6 +124,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, if (ArraySize) { if (Expr *SizeExpr = *ArraySize) { + if (SizeExpr->isValueDependent()) + ExprBits.ValueDependent = true; if (SizeExpr->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (SizeExpr->containsUnexpandedParameterPack()) @@ -134,6 +136,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } if (Initializer) { + if (Initializer->isValueDependent()) + ExprBits.ValueDependent = true; if (Initializer->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (Initializer->containsUnexpandedParameterPack()) @@ -143,6 +147,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } for (unsigned I = 0; I != PlacementArgs.size(); ++I) { + if (PlacementArgs[I]->isValueDependent()) + ExprBits.ValueDependent = true; if (PlacementArgs[I]->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (PlacementArgs[I]->containsUnexpandedParameterPack()) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index fbec3ae582b..ed3d1fa905b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -66,8 +66,6 @@ using llvm::APSInt; using llvm::APFloat; using llvm::Optional; -static bool IsGlobalLValue(APValue::LValueBase B); - namespace { struct LValue; class CallStackFrame; @@ -98,6 +96,9 @@ namespace { if (B.is<TypeInfoLValue>()) return B.getTypeInfoType(); + if (B.is<DynamicAllocLValue>()) + return B.getDynamicAllocType(); + const Expr *Base = B.get<const Expr*>(); // For a materialized temporary, the type of the temporary we materialized @@ -612,8 +613,9 @@ namespace { }; } -static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase, - APValue &Value, QualType T); +static bool HandleDestructorCall(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T); namespace { /// A cleanup, and a flag indicating whether it is lifetime-extended. @@ -629,8 +631,14 @@ namespace { bool isLifetimeExtended() const { return Value.getInt(); } bool endLifetime(EvalInfo &Info, bool RunDestructors) { - if (RunDestructors && T.isDestructedType()) - return HandleDestructorCall(Info, Base, *Value.getPointer(), T); + if (RunDestructors) { + SourceLocation Loc; + if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) + Loc = VD->getLocation(); + else if (const Expr *E = Base.dyn_cast<const Expr*>()) + Loc = E->getExprLoc(); + return HandleDestructorCall(Info, Loc, Base, *Value.getPointer(), T); + } *Value.getPointer() = APValue(); return true; } @@ -742,6 +750,28 @@ namespace { llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase> ObjectsUnderConstruction; + /// A dynamically-allocated heap object. + struct DynAlloc { + /// The value of this heap-allocated object. + APValue Value; + /// The allocating expression; used for diagnostics. + const Expr *AllocExpr = nullptr; + }; + + struct DynAllocOrder { + bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const { + return L.getIndex() < R.getIndex(); + } + }; + + /// Current heap allocations, along with the location where each was + /// allocated. We use std::map here because we need stable addresses + /// for the stored APValues. + std::map<DynamicAllocLValue, DynAlloc, DynAllocOrder> HeapAllocs; + + /// The number of heap allocations performed so far in this evaluation. + unsigned NumHeapAllocs = 0; + struct EvaluatingConstructorRAII { EvalInfo &EI; ObjectUnderConstruction Object; @@ -766,20 +796,20 @@ namespace { struct EvaluatingDestructorRAII { EvalInfo &EI; ObjectUnderConstruction Object; + bool DidInsert; EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object) : EI(EI), Object(Object) { - bool DidInsert = EI.ObjectsUnderConstruction - .insert({Object, ConstructionPhase::Destroying}) - .second; - (void)DidInsert; - assert(DidInsert && "destroyed object multiple times"); + DidInsert = EI.ObjectsUnderConstruction + .insert({Object, ConstructionPhase::Destroying}) + .second; } void startedDestroyingBases() { EI.ObjectsUnderConstruction[Object] = ConstructionPhase::DestroyingBases; } ~EvaluatingDestructorRAII() { - EI.ObjectsUnderConstruction.erase(Object); + if (DidInsert) + EI.ObjectsUnderConstruction.erase(Object); } }; @@ -917,6 +947,16 @@ namespace { return true; } + APValue *createHeapAlloc(const Expr *E, QualType T, LValue &LV); + + Optional<DynAlloc*> lookupDynamicAlloc(DynamicAllocLValue DA) { + Optional<DynAlloc*> Result; + auto It = HeapAllocs.find(DA); + if (It != HeapAllocs.end()) + Result = &It->second; + return Result; + } + void performLifetimeExtension() { // Disable the cleanups for lifetime-extended temporaries. CleanupStack.erase( @@ -1192,12 +1232,17 @@ namespace { Info.CurrentCall->popTempVersion(); } private: - static bool cleanup(EvalInfo &Info, bool RunDestructors, unsigned OldStackSize) { + static bool cleanup(EvalInfo &Info, bool RunDestructors, + unsigned OldStackSize) { + assert(OldStackSize <= Info.CleanupStack.size() && + "running cleanups out of order?"); + // Run all cleanups for a block scope, and non-lifetime-extended cleanups // for a full-expression scope. for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) { - if (!(IsFullExpression && Info.CleanupStack[I-1].isLifetimeExtended())) { - if (!Info.CleanupStack[I-1].endLifetime(Info, RunDestructors)) + if (!(IsFullExpression && + Info.CleanupStack[I - 1].isLifetimeExtended())) { + if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) return false; } } @@ -1634,15 +1679,40 @@ static void negateAsSigned(APSInt &Int) { template<typename KeyT> APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, bool IsLifetimeExtended, LValue &LV) { - unsigned Version = Info.CurrentCall->getTempVersion(); + unsigned Version = getTempVersion(); APValue::LValueBase Base(Key, Index, Version); LV.set(Base); APValue &Result = Temporaries[MapKeyTy(Key, Version)]; assert(Result.isAbsent() && "temporary created multiple times"); - Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended)); + + // If we're creating a temporary immediately in the operand of a speculative + // evaluation, don't register a cleanup to be run outside the speculative + // evaluation context, since we won't actually be able to initialize this + // object. + if (Index <= Info.SpeculativeEvaluationDepth) { + if (T.isDestructedType()) + Info.noteSideEffect(); + } else { + Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended)); + } return Result; } +APValue *EvalInfo::createHeapAlloc(const Expr *E, QualType T, LValue &LV) { + if (NumHeapAllocs > DynamicAllocLValue::getMaxIndex()) { + FFDiag(E, diag::note_constexpr_heap_alloc_limit_exceeded); + return nullptr; + } + + DynamicAllocLValue DA(NumHeapAllocs++); + LV.set(APValue::LValueBase::getDynamicAlloc(DA, T)); + auto Result = HeapAllocs.emplace(std::piecewise_construct, + std::forward_as_tuple(DA), std::tuple<>()); + assert(Result.second && "reused a heap alloc index?"); + Result.first->second.AllocExpr = E; + return &Result.first->second.Value; +} + /// Produce a string describing the given constexpr call. void CallStackFrame::describe(raw_ostream &Out) { unsigned ArgIndex = 0; @@ -1713,7 +1783,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return isa<FunctionDecl>(D); } - if (B.is<TypeInfoLValue>()) + if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>()) return true; const Expr *E = B.get<const Expr*>(); @@ -1812,6 +1882,12 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { Info.Note(VD->getLocation(), diag::note_declared_at); else if (const Expr *E = Base.dyn_cast<const Expr*>()) Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here); + else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) { + // FIXME: Produce a note for dangling pointers too. + if (Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA)) + Info.Note((*Alloc)->AllocExpr->getExprLoc(), + diag::note_constexpr_dynamic_alloc_here); + } // We have no information to show for a typeid(T) object. } @@ -1846,14 +1922,23 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, LVal.getLValueCallIndex() == 0) && "have call index for global lvalue"); + if (Base.is<DynamicAllocLValue>()) { + Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc) + << IsReferenceType << !Designator.Entries.empty(); + NoteLValueLocation(Info, Base); + return false; + } + if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) { if (const VarDecl *Var = dyn_cast<const VarDecl>(VD)) { // Check if this is a thread-local variable. if (Var->getTLSKind()) + // FIXME: Diagnostic! return false; // A dllimport variable never acts like a constant. if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr<DLLImportAttr>()) + // FIXME: Diagnostic! return false; } if (const auto *FD = dyn_cast<const FunctionDecl>(VD)) { @@ -1869,6 +1954,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // perform initialization with the address of the thunk. if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen && FD->hasAttr<DLLImportAttr>()) + // FIXME: Diagnostic! return false; } } @@ -2045,6 +2131,20 @@ static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, Expr::EvaluateForCodeGen); } +/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless +/// "the allocated storage is deallocated within the evaluation". +static bool CheckMemoryLeaks(EvalInfo &Info) { + if (!Info.HeapAllocs.empty()) { + // We can still fold to a constant despite a compile-time memory leak, + // so long as the heap allocation isn't referenced in the result (we check + // that in CheckConstantExpression). + Info.CCEDiag(Info.HeapAllocs.begin()->second.AllocExpr, + diag::note_constexpr_memory_leak) + << unsigned(Info.HeapAllocs.size() - 1); + } + return true; +} + static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. @@ -2736,9 +2836,10 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, // FIXME: This is inefficient; we should probably introduce something similar // to the LLVM ConstantDataArray to make this cheaper. static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S, - APValue &Result) { - const ConstantArrayType *CAT = - Info.Ctx.getAsConstantArrayType(S->getType()); + APValue &Result, + QualType AllocType = QualType()) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? S->getType() : AllocType); assert(CAT && "string literal isn't an array"); QualType CharType = CAT->getElementType(); assert(CharType->isIntegerType() && "unexpected character type"); @@ -3372,6 +3473,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) return CompleteObject(); + } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) { + Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK; + return CompleteObject(); + } + return CompleteObject(LVal.Base, &(*Alloc)->Value, + LVal.Base.getDynamicAllocType()); } else { const Expr *Base = LVal.Base.dyn_cast<const Expr*>(); @@ -5462,6 +5571,18 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, const LValue &This, APValue &Value, QualType T) { + // Objects can only be destroyed while they're within their lifetimes. + // FIXME: We have no representation for whether an object of type nullptr_t + // is in its lifetime; it usually doesn't matter. Perhaps we should model it + // as indeterminate instead? + if (Value.isAbsent() && !T->isNullPtrType()) { + APValue Printable; + This.moveInto(Printable); + Info.FFDiag(CallLoc, diag::note_constexpr_destroy_out_of_lifetime) + << Printable.getAsString(Info.Ctx, Info.Ctx.getLValueReferenceType(T)); + return false; + } + // Invent an expression for location purposes. // FIXME: We shouldn't need to do this. OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue); @@ -5476,10 +5597,16 @@ static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size)) return false; + // Ensure that we have actual array elements available to destroy; the + // destructors might mutate the value, so we can't run them on the array + // filler. + if (Size && Size > Value.getArrayInitializedElts()) + expandArray(Value, Value.getArraySize() - 1); + for (; Size != 0; --Size) { APValue &Elem = Value.getArrayInitializedElt(Size - 1); - if (!HandleDestructorCallImpl(Info, CallLoc, ElemLV, Elem, ElemT) || - !HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1)) + if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) || + !HandleDestructorCallImpl(Info, CallLoc, ElemLV, Elem, ElemT)) return false; } @@ -5505,16 +5632,12 @@ static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, } const CXXDestructorDecl *DD = RD->getDestructor(); - if (!DD) { - // FIXME: Can we get here for a type with an irrelevant destructor? + if (!DD && !RD->hasTrivialDestructor()) { Info.FFDiag(CallLoc); return false; } - const FunctionDecl *Definition = nullptr; - const Stmt *Body = DD->getBody(Definition); - - if ((DD && DD->isTrivial()) || + if (!DD || DD->isTrivial() || (RD->isAnonymousStructOrUnion() && RD->isUnion())) { // A trivial destructor just ends the lifetime of the object. Check for // this case before checking for a body, because we might not bother @@ -5532,6 +5655,9 @@ static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, if (!Info.CheckCallLimit(CallLoc)) return false; + const FunctionDecl *Definition = nullptr; + const Stmt *Body = DD->getBody(Definition); + if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body)) return false; @@ -5542,6 +5668,16 @@ static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, EvalInfo::EvaluatingDestructorRAII EvalObj( Info, ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}); + if (!EvalObj.DidInsert) { + // C++2a [class.dtor]p19: + // the behavior is undefined if the destructor is invoked for an object + // whose lifetime has ended + // (Note that formally the lifetime ends when the period of destruction + // begins, even though certain uses of the object remain valid until the + // period of destruction ends.) + Info.FFDiag(CallLoc, diag::note_constexpr_double_destroy); + return false; + } // FIXME: Creating an APValue just to hold a nonexistent return value is // wasteful. @@ -5598,13 +5734,13 @@ static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc, return true; } -static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase, - APValue &Value, QualType T) { - SourceLocation Loc; - if (const ValueDecl *VD = LVBase.dyn_cast<const ValueDecl*>()) - Loc = VD->getLocation(); - else if (const Expr *E = LVBase.dyn_cast<const Expr*>()) - Loc = E->getExprLoc(); +static bool HandleDestructorCall(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T) { + // If we've had an unmodeled side-effect, we can't rely on mutable state + // (such as the object we're about to destroy) being correct. + if (Info.EvalStatus.HasSideEffects) + return false; LValue LV; LV.set({LVBase}); @@ -7339,6 +7475,8 @@ public: return true; } + bool VisitCXXNewExpr(const CXXNewExpr *E); + bool VisitSourceLocExpr(const SourceLocExpr *E) { assert(E->isStringType() && "SourceLocExpr isn't a pointer type?"); APValue LValResult = E->EvaluateInContext( @@ -7900,6 +8038,125 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType); + +bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorNew = E->getOperatorNew(); + if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa<CXXMethodDecl>(OperatorNew) << OperatorNew; + return false; + } + + // FIXME: There is no restriction on this, but it's not clear that it + // makes any sense. We get here for cases such as: + // + // new (std::align_val_t{N}) X(int) + // + // (which should presumably be valid only if N is a multiple of + // alignof(int). + if (E->getNumPlacementArgs()) + return Error(E, diag::note_constexpr_new_placement); + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + + const Expr *Init = E->getInitializer(); + const InitListExpr *ResizedArrayILE = nullptr; + + QualType AllocType = E->getAllocatedType(); + if (Optional<const Expr*> ArraySize = E->getArraySize()) { + const Expr *Stripped = *ArraySize; + for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped); + Stripped = ICE->getSubExpr()) + if (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_IntegralCast) + break; + + llvm::APSInt ArrayBound; + if (!EvaluateInteger(Stripped, ArrayBound, Info)) + return false; + + // C++ [expr.new]p9: + // The expression is erroneous if: + // -- [...] its value before converting to size_t [or] applying the + // second standard conversion sequence is less than zero + if (ArrayBound.isSigned() && ArrayBound.isNegative()) { + Info.FFDiag(*ArraySize, diag::note_constexpr_new_negative) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- its value is such that the size of the allocated object would + // exceed the implementation-defined limit + if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType, + ArrayBound) > + ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- the new-initializer is a braced-init-list and the number of + // array elements for which initializers are provided [...] + // exceeds the number of elements to initialize + if (Init) { + auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType()); + assert(CAT && "unexpected type for array initializer"); + + unsigned Bits = + std::max(CAT->getSize().getBitWidth(), ArrayBound.getBitWidth()); + llvm::APInt InitBound = CAT->getSize().zextOrSelf(Bits); + llvm::APInt AllocBound = ArrayBound.zextOrSelf(Bits); + if (InitBound.ugt(AllocBound)) { + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_small) + << AllocBound.toString(10, /*Signed=*/false) + << InitBound.toString(10, /*Signed=*/false) + << (*ArraySize)->getSourceRange(); + return false; + } + + // If the sizes differ, we must have an initializer list, and we need + // special handling for this case when we initialize. + if (InitBound != AllocBound) + ResizedArrayILE = cast<InitListExpr>(Init); + } + + AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, + ArrayType::Normal, 0); + } else { + assert(!AllocType->isArrayType() && + "array allocation with non-array new"); + } + + // Perform the allocation and obtain a pointer to the resulting object. + APValue *Val = Info.createHeapAlloc(E, AllocType, Result); + if (!Val) + return false; + + if (ResizedArrayILE) { + if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, + AllocType)) + return false; + } else if (Init) { + if (!EvaluateInPlace(*Val, Info, Result, Init)) + return false; + } else { + *Val = getDefaultInitValue(AllocType); + } + + // Array new returns a pointer to the first element, not a pointer to the + // array. + if (auto *AT = AllocType->getAsArrayTypeUnsafe()) + Result.addArray(Info, E, cast<ConstantArrayType>(AT)); + + return true; +} //===----------------------------------------------------------------------===// // Member Pointer Evaluation //===----------------------------------------------------------------------===// @@ -8367,9 +8624,8 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) { const CXXRecordDecl *ClosureClass = E->getLambdaClass(); - if (ClosureClass->isInvalidDecl()) return false; - - if (Info.checkingPotentialConstantExpression()) return true; + if (ClosureClass->isInvalidDecl()) + return false; const size_t NumFields = std::distance(ClosureClass->field_begin(), ClosureClass->field_end()); @@ -8688,14 +8944,16 @@ namespace { bool VisitCallExpr(const CallExpr *E) { return handleCallExpr(E, Result, &This); } - bool VisitInitListExpr(const InitListExpr *E); + bool VisitInitListExpr(const InitListExpr *E, + QualType AllocType = QualType()); bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, const LValue &Subobject, APValue *Value, QualType Type); - bool VisitStringLiteral(const StringLiteral *E) { - expandStringLiteral(Info, E, Result); + bool VisitStringLiteral(const StringLiteral *E, + QualType AllocType = QualType()) { + expandStringLiteral(Info, E, Result, AllocType); return true; } }; @@ -8707,6 +8965,15 @@ static bool EvaluateArray(const Expr *E, const LValue &This, return ArrayExprEvaluator(Info, This, Result).Visit(E); } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType) { + assert(ILE->isRValue() && ILE->getType()->isArrayType() && + "not an array rvalue"); + return ArrayExprEvaluator(Info, This, Result) + .VisitInitListExpr(ILE, AllocType); +} + // Return true iff the given array filler may depend on the element index. static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { // For now, just whitelist non-class value-initialization and initialization @@ -8723,15 +8990,23 @@ static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { return true; } -bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); +bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, + QualType AllocType) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? E->getType() : AllocType); if (!CAT) return Error(E); // C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...] // an appropriately-typed string literal enclosed in braces. - if (E->isStringLiteralInit()) - return Visit(E->getInit(0)); + if (E->isStringLiteralInit()) { + auto *SL = dyn_cast<StringLiteral>(E->getInit(0)->IgnoreParens()); + // FIXME: Support ObjCEncodeExpr here once we support it in + // ArrayExprEvaluator generally. + if (!SL) + return Error(E); + return VisitStringLiteral(SL, AllocType); + } bool Success = true; @@ -9415,6 +9690,8 @@ static QualType getObjectType(APValue::LValueBase B) { return E->getType(); } else if (B.is<TypeInfoLValue>()) { return B.getTypeInfoType(); + } else if (B.is<DynamicAllocLValue>()) { + return B.getDynamicAllocType(); } return QualType(); @@ -12402,9 +12679,116 @@ public: return true; } } + + bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); }; } // end anonymous namespace +static bool hasVirtualDestructor(QualType T) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual(); + return false; +} + +bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorDelete = E->getOperatorDelete(); + if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete; + return false; + } + + const Expr *Arg = E->getArgument(); + + LValue Pointer; + if (!EvaluatePointer(Arg, Pointer, Info)) + return false; + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) { + // This is the only case where we need to produce an extension warning: + // the only other way we can succeed is if we find a dynamic allocation, + // and we will have warned when we allocated it in that case. + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + return true; + } + + auto PointerAsString = [&] { + APValue Printable; + Pointer.moveInto(Printable); + return Printable.getAsString(Info.Ctx, Arg->getType()); + }; + + DynamicAllocLValue DA = Pointer.Base.dyn_cast<DynamicAllocLValue>(); + if (!DA) { + Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc) + << PointerAsString(); + if (Pointer.Base) + NoteLValueLocation(Info, Pointer.Base); + return false; + } + QualType AllocType = Pointer.Base.getDynamicAllocType(); + + Optional<EvalInfo::DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_double_delete); + return false; + } + + if (E->isArrayForm() != AllocType->isConstantArrayType()) { + Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) + << E->isArrayForm() << AllocType; + NoteLValueLocation(Info, Pointer.Base); + return false; + } + + bool Subobject = false; + if (E->isArrayForm()) { + Subobject = Pointer.Designator.Entries.size() != 1 || + Pointer.Designator.Entries[0].getAsArrayIndex() != 0; + } else { + Subobject = Pointer.Designator.MostDerivedPathLength != 0 || + Pointer.Designator.isOnePastTheEnd(); + } + if (Subobject) { + Info.FFDiag(E, diag::note_constexpr_delete_subobject) + << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); + return false; + } + + // For the non-array case, the designator must be empty if the static type + // does not have a virtual destructor. + if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 && + !hasVirtualDestructor(Arg->getType()->getPointeeType())) { + Info.FFDiag(E, diag::note_constexpr_delete_base_nonvirt_dtor) + << Arg->getType()->getPointeeType() << AllocType; + return false; + } + + if (!HandleDestructorCall(Info, E->getExprLoc(), Pointer.getLValueBase(), + (*Alloc)->Value, AllocType)) + return false; + + if (!Info.HeapAllocs.erase(DA)) { + // The element was already erased. This means the destructor call also + // deleted the object. + // FIXME: This probably results in undefined behavior before we get this + // far, and should be diagnosed elsewhere first. + Info.FFDiag(E, diag::note_constexpr_double_delete); + return false; + } + + return true; +} + static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { assert(E->isRValue() && E->getType()->isVoidType()); return VoidExprEvaluator(Info).Visit(E); @@ -12554,7 +12938,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { } // Check this core constant expression is a constant expression. - return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result); + return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result) && + CheckMemoryLeaks(Info); } static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result, @@ -12723,14 +13108,15 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this)) + if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) return false; if (!Info.discardCleanups()) llvm_unreachable("Unhandled cleanup; missing full expression marker?"); return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val, - Usage); + Usage) && + CheckMemoryLeaks(Info); } bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, @@ -12799,7 +13185,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, if (!Info.discardCleanups()) llvm_unreachable("Unhandled cleanup; missing full expression marker?"); - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); + return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) && + CheckMemoryLeaks(Info); } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be @@ -13409,7 +13796,7 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result, ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && // FIXME: We don't produce a diagnostic for this, but the callers that // call us on arbitrary full-expressions should generally not care. - Info.discardCleanups(); + Info.discardCleanups() && !Status.HasSideEffects; if (!Diags.empty()) { IsConstExpr = false; |

