Skip to content

Commit 3eaf4a9

Browse files
authored
[clang][bytecode] Check for memory leaks after destroying global scope (#112868)
The global scope we create when evaluating expressions might free some of the dynamic memory allocations, so we can't check for memory leaks before destroying it.
1 parent d508701 commit 3eaf4a9

File tree

6 files changed

+40
-25
lines changed

6 files changed

+40
-25
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4132,10 +4132,16 @@ template <class Emitter>
41324132
bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
41334133
LocalScope<Emitter> RootScope(this);
41344134

4135+
// If we won't destroy the toplevel scope, check for memory leaks first.
4136+
if (!DestroyToplevelScope) {
4137+
if (!this->emitCheckAllocations(E))
4138+
return false;
4139+
}
4140+
41354141
auto maybeDestroyLocals = [&]() -> bool {
41364142
if (DestroyToplevelScope)
4137-
return RootScope.destroyLocals();
4138-
return true;
4143+
return RootScope.destroyLocals() && this->emitCheckAllocations(E);
4144+
return this->emitCheckAllocations(E);
41394145
};
41404146

41414147
// Void expressions.
@@ -4171,8 +4177,7 @@ bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
41714177
return this->emitRetValue(E) && maybeDestroyLocals();
41724178
}
41734179

4174-
(void)maybeDestroyLocals();
4175-
return false;
4180+
return maybeDestroyLocals() && this->emitCheckAllocations(E) && false;
41764181
}
41774182

41784183
template <class Emitter>
@@ -4214,7 +4219,8 @@ bool Compiler<Emitter>::visitDeclAndReturn(const VarDecl *VD,
42144219
DeclScope<Emitter> LS(this, VD);
42154220
if (!this->visit(VD->getAnyInitializer()))
42164221
return false;
4217-
return this->emitRet(VarT.value_or(PT_Ptr), VD) && LS.destroyLocals();
4222+
return this->emitRet(VarT.value_or(PT_Ptr), VD) && LS.destroyLocals() &&
4223+
this->emitCheckAllocations(VD);
42184224
}
42194225

42204226
LocalScope<Emitter> VDScope(this, VD);
@@ -4260,7 +4266,7 @@ bool Compiler<Emitter>::visitDeclAndReturn(const VarDecl *VD,
42604266
return false;
42614267
}
42624268

4263-
return VDScope.destroyLocals();
4269+
return VDScope.destroyLocals() && this->emitCheckAllocations(VD);
42644270
}
42654271

42664272
template <class Emitter>

clang/lib/AST/ByteCode/Context.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result,
7878
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
7979

8080
auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false,
81-
/*DestroyToplevelScope=*/Kind ==
82-
ConstantExprKind::ClassTemplateArgument);
81+
/*DestroyToplevelScope=*/true);
8382
if (Res.isInvalid()) {
8483
C.cleanup();
8584
Stk.clearTo(StackSizeBefore);

clang/lib/AST/ByteCode/EvalEmitter.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,10 @@ bool EvalEmitter::fallthrough(const LabelTy &Label) {
132132
return true;
133133
}
134134

135-
static bool checkReturnState(InterpState &S) {
136-
return S.maybeDiagnoseDanglingAllocations();
137-
}
138-
139135
template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
140136
if (!isActive())
141137
return true;
142138

143-
if (!checkReturnState(S))
144-
return false;
145-
146139
using T = typename PrimConv<OpType>::T;
147140
EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
148141
return true;
@@ -159,9 +152,6 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
159152
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
160153
return false;
161154

162-
if (!checkReturnState(S))
163-
return false;
164-
165155
// Implicitly convert lvalue to rvalue, if requested.
166156
if (ConvertResultToRValue) {
167157
if (!Ptr.isZero() && !Ptr.isDereferencable())
@@ -194,16 +184,12 @@ template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
194184
if (!isActive())
195185
return true;
196186

197-
if (!checkReturnState(S))
198-
return false;
199187
// Function pointers cannot be converted to rvalues.
200188
EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
201189
return true;
202190
}
203191

204192
bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
205-
if (!checkReturnState(S))
206-
return false;
207193
EvalResult.setValid();
208194
return true;
209195
}
@@ -216,9 +202,6 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
216202
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
217203
return false;
218204

219-
if (!checkReturnState(S))
220-
return false;
221-
222205
if (std::optional<APValue> APV =
223206
Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
224207
EvalResult.setValue(*APV);

clang/lib/AST/ByteCode/Interp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3007,6 +3007,10 @@ static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
30073007
return true;
30083008
}
30093009

3010+
static inline bool CheckAllocations(InterpState &S, CodePtr OpPC) {
3011+
return S.maybeDiagnoseDanglingAllocations();
3012+
}
3013+
30103014
/// Check if the initializer and storage types of a placement-new expression
30113015
/// match.
30123016
bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,3 +836,4 @@ def CheckNewTypeMismatchArray : Opcode {
836836
}
837837

838838
def IsConstantContext: Opcode;
839+
def CheckAllocations : Opcode;

clang/test/AST/ByteCode/new-delete.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,28 @@ static_assert(virt_delete(false)); // both-error {{not an integral constant expr
796796
// both-note {{in call to}}
797797

798798

799+
namespace ToplevelScopeInTemplateArg {
800+
class string {
801+
public:
802+
char *mem;
803+
constexpr string() {
804+
this->mem = new char(1);
805+
}
806+
constexpr ~string() {
807+
delete this->mem;
808+
}
809+
constexpr unsigned size() const { return 4; }
810+
};
811+
812+
813+
template <unsigned N>
814+
void test() {};
815+
816+
void f() {
817+
test<string().size()>();
818+
static_assert(string().size() == 4);
819+
}
820+
}
799821

800822
#else
801823
/// Make sure we reject this prior to C++20

0 commit comments

Comments
 (0)