Skip to content

Commit

Permalink
Merge pull request #4788 from 9rnsr/fix9383
Browse files Browse the repository at this point in the history
Issue 9383 - Wrong context for contracts if closure [dis]appears in override function
  • Loading branch information
WalterBright committed Jul 6, 2015
2 parents e03bfc2 + 47d9320 commit 8e87814
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 5 deletions.
6 changes: 3 additions & 3 deletions src/declaration.c
Original file line number Diff line number Diff line change
Expand Up @@ -1810,10 +1810,10 @@ bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
break;
}

if (fdthis->ident != Id::ensure)
if (fdthis->ident != Id::require && fdthis->ident != Id::ensure)
{
/* __ensure is always called directly,
* so it never becomes closure.
/* __require and __ensure will always get called directly,
* so they never make outer functions closure.
*/

//printf("\tfdv = %s\n", fdv->toChars());
Expand Down
22 changes: 21 additions & 1 deletion src/e2ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,8 +958,28 @@ elem *toElem(Expression *e, IRState *irs)
elem *ethis = getEthis(se->loc, irs, fd);
ethis = el_una(OPaddr, TYnptr, ethis);

/* Bugzilla 9383: If 's' is a virtual function parameter
* placed in closure, and actually accessed from in/out
* contract, instead look at the original stack data.
*/
bool forceStackAccess = false;
if (s->Sclass == SCparameter &&
fd->isVirtual() && (fd->fdrequire || fd->fdensure))
{
Dsymbol *sx = irs->getFunc();
while (sx != fd)
{
if (sx->ident == Id::require || sx->ident == Id::ensure)
{
forceStackAccess = true;
break;
}
sx = sx->toParent2();
}
}

int soffset;
if (v && v->offset)
if (v && v->offset && !forceStackAccess)
soffset = v->offset;
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/toir.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd)
/* Going down one nesting level, i.e. we're calling
* a nested function from its enclosing function.
*/
if (irs->sclosure)
if (irs->sclosure && !(fd->ident == Id::require || fd->ident == Id::ensure))
{
ethis = el_var(irs->sclosure);
}
Expand Down
87 changes: 87 additions & 0 deletions test/runnable/testcontracts.d
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,92 @@ void test8093()
{ auto n = foo_val2(); assert(&n !is &g && n == 10); }
}

/*******************************************/
// 9383

class A9383
{
static void delegate() dg;
static int val;

void failInBase() { assert(typeid(this) is typeid(A9383)); }

// in-contract tests
void foo1(int i) in { A9383.val = i; failInBase; } body { } // no closure
void foo2(int i) in { A9383.val = i; failInBase; } body { int x; dg = { ++x; }; } // closure [local]
void foo3(int i) in { A9383.val = i; failInBase; } body { dg = { ++i; }; } // closure [parameter]
void foo4(int i) in { A9383.val = i; failInBase; } body { } // no closure
void foo5(int i) in { A9383.val = i; failInBase; } body { } // no closure
void foo6(int i) in { A9383.val = i; failInBase; } body { int x; dg = { ++x; }; } // closure [local]
void foo7(int i) in { A9383.val = i; failInBase; } body { dg = { ++i; }; } // closure [parameter]

// out-contract tests
void bar1(int i) out { A9383.val = i; } body { } // no closure
void bar2(int i) out { A9383.val = i; } body { int x; dg = { ++x; }; } // closure [local]
void bar3(int i) out { A9383.val = i; } body { dg = { ++i; }; } // closure [parameter]
void bar4(int i) out { A9383.val = i; } body { } // no closure
void bar5(int i) out { A9383.val = i; } body { } // no closure
void bar6(int i) out { A9383.val = i; } body { int x; dg = { ++x; }; } // closure [local]
void bar7(int i) out { A9383.val = i; } body { dg = { ++i; }; } // closure [parameter]
}

class B9383 : A9383
{
static int val;

// in-contract tests
override void foo1(int i) in { B9383.val = i; } body { } // -> no closure
override void foo2(int i) in { B9383.val = i; } body { int x; dg = { ++x; }; } // -> closure [local] appears
override void foo3(int i) in { B9383.val = i; } body { dg = { ++i; }; } // -> closure [parameter]
override void foo4(int i) in { B9383.val = i; } body { int x; dg = { ++x; }; } // -> closure [local] appears
override void foo5(int i) in { B9383.val = i; } body { dg = { ++i; }; } // -> closure [parameter] appears
override void foo6(int i) in { B9383.val = i; } body { } // -> closure [local] disappears
override void foo7(int i) in { B9383.val = i; } body { } // -> closure [parameter] disappears

// out-contract tests
override void bar1(int i) out { B9383.val = i; } body { } // -> no closure
override void bar2(int i) out { B9383.val = i; } body { int x; dg = { ++x; }; } // -> closure [local] appears
override void bar3(int i) out { B9383.val = i; } body { dg = { ++i; }; } // -> closure [parameter]
override void bar4(int i) out { B9383.val = i; } body { int x; dg = { ++x; }; } // -> closure [local] appears
override void bar5(int i) out { B9383.val = i; } body { dg = { ++i; }; } // -> closure [parameter] appears
override void bar6(int i) out { B9383.val = i; } body { } // -> closure [local] disappears
override void bar7(int i) out { B9383.val = i; } body { } // -> closure [parameter] disappears
}

void test9383()
{
auto a = new A9383();
auto b = new B9383();

// base class in-contract is used from derived class. // base derived
b.foo1(101); assert(A9383.val == 101 && B9383.val == 101); // no closure -> no closure
b.foo2(102); assert(A9383.val == 102 && B9383.val == 102); // closure [local] -> closure [local] appears
b.foo3(103); assert(A9383.val == 103 && B9383.val == 103); // closure [parameter] -> closure [parameter]
b.foo4(104); assert(A9383.val == 104 && B9383.val == 104); // no closure -> closure [local] appears
b.foo5(105); assert(A9383.val == 105 && B9383.val == 105); // no closure -> closure [parameter] appears
b.foo6(106); assert(A9383.val == 106 && B9383.val == 106); // closure [local] -> closure [local] disappears
b.foo7(107); assert(A9383.val == 107 && B9383.val == 107); // closure [parameter] -> closure [parameter] disappears

// base class out-contract is used from derived class. // base derived
b.bar1(101); assert(A9383.val == 101 && B9383.val == 101); // no closure -> no closure
b.bar2(102); assert(A9383.val == 102 && B9383.val == 102); // closure [local] -> closure [local] appears
b.bar3(103); assert(A9383.val == 103 && B9383.val == 103); // closure [parameter] -> closure [parameter]
b.bar4(104); assert(A9383.val == 104 && B9383.val == 104); // no closure -> closure [local] appears
b.bar5(105); assert(A9383.val == 105 && B9383.val == 105); // no closure -> closure [parameter] appears
b.bar6(106); assert(A9383.val == 106 && B9383.val == 106); // closure [local] -> closure [local] disappears
b.bar7(107); assert(A9383.val == 107 && B9383.val == 107); // closure [parameter] -> closure [parameter] disappears

// in-contract in base class.
a.foo1(101); assert(A9383.val == 101); // no closure
a.foo2(102); assert(A9383.val == 102); // closure [local]
a.foo3(103); assert(A9383.val == 103); // closure [parameter]

// out-contract in base class.
a.bar1(101); assert(A9383.val == 101); // no closure
a.bar2(102); assert(A9383.val == 102); // closure [local]
a.bar3(103); assert(A9383.val == 103); // closure [parameter]
}

/*******************************************/
// 10479

Expand Down Expand Up @@ -685,6 +771,7 @@ int main()
test7218();
test8073();
test8093();
test9383();

printf("Success\n");
return 0;
Expand Down

0 comments on commit 8e87814

Please sign in to comment.