| //===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "InterpState.h" |
| #include "InterpFrame.h" |
| #include "InterpStack.h" |
| #include "Program.h" |
| #include "State.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclTemplate.h" |
| |
| using namespace clang; |
| using namespace clang::interp; |
| |
| InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, |
| Context &Ctx, SourceMapper *M) |
| : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), |
| Current(&BottomFrame) { |
| InConstantContext = Parent.InConstantContext; |
| CheckingPotentialConstantExpression = |
| Parent.CheckingPotentialConstantExpression; |
| CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; |
| } |
| |
| InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, |
| Context &Ctx, const Function *Func) |
| : Parent(Parent), M(nullptr), P(P), Stk(Stk), Ctx(Ctx), |
| BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()), |
| Current(&BottomFrame) { |
| InConstantContext = Parent.InConstantContext; |
| CheckingPotentialConstantExpression = |
| Parent.CheckingPotentialConstantExpression; |
| CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; |
| } |
| |
| bool InterpState::inConstantContext() const { |
| if (ConstantContextOverride) |
| return *ConstantContextOverride; |
| |
| return InConstantContext; |
| } |
| |
| InterpState::~InterpState() { |
| while (Current && !Current->isBottomFrame()) { |
| InterpFrame *Next = Current->Caller; |
| delete Current; |
| Current = Next; |
| } |
| BottomFrame.destroyScopes(); |
| |
| while (DeadBlocks) { |
| DeadBlock *Next = DeadBlocks->Next; |
| |
| // There might be a pointer in a global structure pointing to the dead |
| // block. |
| for (Pointer *P = DeadBlocks->B.Pointers; P; P = P->asBlockPointer().Next) |
| DeadBlocks->B.removePointer(P); |
| |
| std::free(DeadBlocks); |
| DeadBlocks = Next; |
| } |
| } |
| |
| void InterpState::cleanup() { |
| // As a last resort, make sure all pointers still pointing to a dead block |
| // don't point to it anymore. |
| if (Alloc) |
| Alloc->cleanup(); |
| } |
| |
| Frame *InterpState::getCurrentFrame() { return Current; } |
| |
| void InterpState::deallocate(Block *B) { |
| assert(B); |
| assert(!B->isDynamic()); |
| assert(!B->isStatic()); |
| assert(!B->isDead()); |
| |
| // The block might have a pointer saved in a field in its data |
| // that points to the block itself. We call the dtor first, |
| // which will destroy all the data but leave InlineDescriptors |
| // intact. If the block THEN still has pointers, we create a |
| // DeadBlock for it. |
| if (B->IsInitialized) |
| B->invokeDtor(); |
| |
| assert(!B->isInitialized()); |
| if (B->hasPointers()) { |
| size_t Size = B->getSize(); |
| // Allocate a new block, transferring over pointers. |
| char *Memory = |
| reinterpret_cast<char *>(std::malloc(sizeof(DeadBlock) + Size)); |
| auto *D = new (Memory) DeadBlock(DeadBlocks, B); |
| // Since the block doesn't hold any actual data anymore, we can just |
| // memcpy() everything over. |
| std::memcpy(D->rawData(), B->rawData(), Size); |
| D->B.IsInitialized = false; |
| } |
| } |
| |
| bool InterpState::maybeDiagnoseDanglingAllocations() { |
| if (!Alloc) |
| return true; |
| |
| bool NoAllocationsLeft = !Alloc->hasAllocations(); |
| |
| if (!checkingPotentialConstantExpression()) { |
| for (const auto &[Source, Site] : Alloc->allocation_sites()) { |
| assert(!Site.empty()); |
| |
| CCEDiag(Source->getExprLoc(), diag::note_constexpr_memory_leak) |
| << (Site.size() - 1) << Source->getSourceRange(); |
| } |
| } |
| // Keep evaluating before C++20, since the CXXNewExpr wasn't valid there |
| // in the first place. |
| return NoAllocationsLeft || !getLangOpts().CPlusPlus20; |
| } |
| |
| StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const { |
| for (const InterpFrame *F = Current; F; F = F->Caller) { |
| const Function *Func = F->getFunction(); |
| if (!Func) |
| continue; |
| const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl()); |
| if (!MD) |
| continue; |
| const IdentifierInfo *FnII = MD->getIdentifier(); |
| if (!FnII || !FnII->isStr(Name)) |
| continue; |
| |
| const auto *CTSD = |
| dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent()); |
| if (!CTSD) |
| continue; |
| |
| const IdentifierInfo *ClassII = CTSD->getIdentifier(); |
| const TemplateArgumentList &TAL = CTSD->getTemplateArgs(); |
| if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") && |
| TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) { |
| QualType ElemType = TAL[0].getAsType(); |
| const auto *NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC())); |
| return {NewCall, ElemType}; |
| } |
| } |
| |
| return {}; |
| } |