Skip to content

Commit 7601a14

Browse files
committed
[clang][bytecode] Check for memory leaks after destroying global scope
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 228f88f commit 7601a14

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)