Skip to content

Commit

Permalink
fix Issue 15044 - destroy might leak memory
Browse files Browse the repository at this point in the history
Defer semantic3 running of generated `opAssign` function, and add special
error gagging mechanism in `FuncDeclaration.semantic3()` to hide errors
from its body.
  • Loading branch information
9rnsr committed Sep 14, 2015
1 parent ea5d85a commit 8e46763
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class StructDeclaration : public AggregateDeclaration
bool hasIdentityEquals; // true if has identity opEquals
FuncDeclarations postblits; // Array of postblit functions
FuncDeclaration *postblit; // aggregate postblit
FuncDeclaration *fdassign; // generated opAssign function

FuncDeclaration *xeq; // TypeInfo_Struct.xopEquals
FuncDeclaration *xcmp; // TypeInfo_Struct.xopCmp
Expand Down
3 changes: 2 additions & 1 deletion src/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc)
sd->members->push(fop);
fop->addMember(sc, sd);
sd->hasIdentityAssign = true; // temporary mark identity assignable
sd->fdassign = fop;

unsigned errors = global.startGagging(); // Do not report errors, even if the
Scope *sc2 = sc->push();
Expand All @@ -310,7 +311,7 @@ FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc)

fop->semantic(sc2);
fop->semantic2(sc2);
fop->semantic3(sc2);
// Bugzilla 15044: fop->semantic3 isn't run here for lazy forward reference resolution.

sc2->pop();
if (global.endGagging(errors)) // if errors happened
Expand Down
37 changes: 35 additions & 2 deletions src/func.c
Original file line number Diff line number Diff line change
Expand Up @@ -1226,7 +1226,6 @@ void FuncDeclaration::semantic3(Scope *sc)
{
VarDeclaration *argptr = NULL;
VarDeclaration *_arguments = NULL;
int nerrors = global.errors;

if (!parent)
{
Expand All @@ -1243,6 +1242,30 @@ void FuncDeclaration::semantic3(Scope *sc)
//{ static int x; if (++x == 2) *(char*)0=0; }
//printf("\tlinkage = %d\n", sc->linkage);

if (ident == Id::assign && !inuse)
{
AggregateDeclaration *ad = isThis();
StructDeclaration *sd = ad ? ad->isStructDeclaration() : NULL;
if (sd && sd->fdassign == this)
{
/* Bugzilla 15044: For generated opAssign function, any errors
* from its body need to be gagged.
*/
unsigned oldErrors = global.startGagging();
++inuse;
semantic3(sc);
--inuse;
if (global.endGagging(oldErrors)) // if errors happened
{
// Disable generated opAssign, because some members forbid identity assignment.
storage_class |= STCdisable;
fbody = NULL; // remove fbody which contains the error
semantic3Errors = false;
}
return;
}
}

//printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
if (semanticRun >= PASSsemantic3)
return;
Expand All @@ -1261,6 +1284,8 @@ void FuncDeclaration::semantic3(Scope *sc)
return;
}

unsigned oldErrors = global.errors;

if (frequire)
{
for (size_t i = 0; i < foverrides.dim; i++)
Expand Down Expand Up @@ -2189,7 +2214,7 @@ void FuncDeclaration::semantic3(Scope *sc)
* Otherwise, error gagging should be temporarily ungagged by functionSemantic3.
*/
semanticRun = PASSsemantic3done;
semantic3Errors = (global.errors != nerrors) || (fbody && fbody->isErrorStatement());
semantic3Errors = (global.errors != oldErrors) || (fbody && fbody->isErrorStatement());
if (type->ty == Terror)
errors = true;
//printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars());
Expand Down Expand Up @@ -2237,6 +2262,14 @@ bool FuncDeclaration::functionSemantic()
return functionSemantic3();
}

if (ident == Id::assign)
{
AggregateDeclaration *ad = isThis();
StructDeclaration *sd = ad ? ad->isStructDeclaration() : NULL;
if (sd && sd->fdassign == this)
return functionSemantic3();
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions src/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id)
hasIdentityAssign = false;
hasIdentityEquals = false;
postblit = NULL;
fdassign = NULL;

xeq = NULL;
xcmp = NULL;
Expand Down
32 changes: 32 additions & 0 deletions test/fail_compilation/fail15044.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail15044.d(30): Error: function fail15044.V.opAssign is not callable because it is annotated with @disable
---
*/

struct S
{
void opAssign(S) {}
}

struct V
{
// `s` has opAssign, so struct V needs to generate member-wise opAssign.
// But S.opAssign is not callable on const object, so V.opAssign should be
// @disable.
const S s;

// Here, the initializer of x is evaluated in V.semantic2. But
// V.opAssign.semantic3 is not yet invoked, so its attribute should be
// lazily inferred in functionSemantic even though it's non-instantiated function.
enum int x = ()
{
// Here, the initializer of x is evaluated in V.semantic2, and
// V.opAssign.semantic3 is not yet invoked in this time.
// Therefore its @disable attribute needs to be inferred by
// functionSemantic, even though it's non-instantiated function.
V v;
v = v;
}();
}
33 changes: 33 additions & 0 deletions test/runnable/testassign.d
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,39 @@ void test14672()
assert(ba[0] is d);
}

/***************************************************/
// 15044

void destroy15044(T)(ref T obj)
{
static if (__traits(hasMember, T, "__xdtor"))
obj.__xdtor();
else
static assert(0, T.stringof);
}

struct V15044
{
~this()
{
}

RC15044!V15044 dup()
{
return RC15044!V15044(&this);
}
}

struct RC15044(T)
{
~this()
{
destroy15044(*t);
static assert(__traits(hasMember, T, "__xdtor"));
}
T* t;
}

/***************************************************/

int main()
Expand Down

0 comments on commit 8e46763

Please sign in to comment.