Index: clang/lib/AST/Interp/InterpFrame.h =================================================================== --- clang/lib/AST/Interp/InterpFrame.h +++ clang/lib/AST/Interp/InterpFrame.h @@ -47,6 +47,11 @@ /// Invokes the destructors for a scope. void destroy(unsigned Idx); + /// Invokes the destructors for all scopes. This is used in the + /// InterpFrame destructor to avoid memory leaks in case the + /// interpretation was not successful. + void destroyAll(); + /// Pops the arguments off the stack. void popArgs(); Index: clang/lib/AST/Interp/InterpFrame.cpp =================================================================== --- clang/lib/AST/Interp/InterpFrame.cpp +++ clang/lib/AST/Interp/InterpFrame.cpp @@ -64,6 +64,7 @@ } InterpFrame::~InterpFrame() { + destroyAll(); for (auto &Param : Params) S.deallocate(reinterpret_cast(Param.second.get())); } @@ -74,6 +75,16 @@ } } +void InterpFrame::destroyAll() { + if (!Func) + return; + for (const Scope &Sc : Func->scopes()) { + for (auto &Local : Sc.locals()) { + S.destroy(reinterpret_cast(localBlock(Local.Offset))); + } + } +} + void InterpFrame::popArgs() { for (PrimType Ty : Func->args_reverse()) TYPE_SWITCH(Ty, S.Stk.discard()); Index: clang/lib/AST/Interp/InterpState.h =================================================================== --- clang/lib/AST/Interp/InterpState.h +++ clang/lib/AST/Interp/InterpState.h @@ -78,9 +78,12 @@ /// Reports overflow and return true if evaluation should continue. bool reportOverflow(const Expr *E, const llvm::APSInt &Value); - /// Deallocates a pointer. + /// Deallocates a block. void deallocate(Block *B); + /// Destroys a block, used during tear down. + void destroy(Block *B); + /// Delegates source mapping to the mapper. SourceInfo getSource(const Function *F, CodePtr PC) const override { return M ? M->getSource(F, PC) : F->getSource(PC); Index: clang/lib/AST/Interp/InterpState.cpp =================================================================== --- clang/lib/AST/Interp/InterpState.cpp +++ clang/lib/AST/Interp/InterpState.cpp @@ -54,6 +54,15 @@ return noteUndefinedBehavior(); } +void InterpState::destroy(Block *B) { + assert(B); + Descriptor *Desc = B->getDescriptor(); + assert(Desc); + // Free storage, if necessary. + if (Desc->DtorFn) + Desc->DtorFn(B, B->data(), Desc); +} + void InterpState::deallocate(Block *B) { Descriptor *Desc = B->getDescriptor(); if (B->hasPointers()) { @@ -68,7 +77,6 @@ Desc->MoveFn(B, B->data(), D->data(), Desc); } else { // Free storage, if necessary. - if (Desc->DtorFn) - Desc->DtorFn(B, B->data(), Desc); + destroy(B); } } Index: clang/test/AST/Interp/cxx20.cpp =================================================================== --- clang/test/AST/Interp/cxx20.cpp +++ clang/test/AST/Interp/cxx20.cpp @@ -304,3 +304,15 @@ } static_assert(testInc2() == 1, ""); }; + +constexpr int NoLeakHere() { + int abc[2]; + + abc[0] = 1; + return abc[1]; // expected-note {{read of object outside its lifetime}} \ + // ref-note {{read of uninitialized object}} +} +static_assert(NoLeakHere() == 3); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}}