diff --git a/src/declaration.c b/src/declaration.c index 3bdec1e11e99..e61ae76fa606 100644 --- a/src/declaration.c +++ b/src/declaration.c @@ -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()); diff --git a/src/e2ir.c b/src/e2ir.c index 2cbb49054f03..fdd30b63748d 100644 --- a/src/e2ir.c +++ b/src/e2ir.c @@ -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 { diff --git a/src/toir.c b/src/toir.c index 5a9cd5532b1d..29cd924e7796 100644 --- a/src/toir.c +++ b/src/toir.c @@ -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); } diff --git a/test/runnable/testcontracts.d b/test/runnable/testcontracts.d index 435bd2bc262d..0092d3a62e21 100644 --- a/test/runnable/testcontracts.d +++ b/test/runnable/testcontracts.d @@ -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 @@ -685,6 +771,7 @@ int main() test7218(); test8073(); test8093(); + test9383(); printf("Success\n"); return 0;