Skip to content

Commit

Permalink
Merge pull request #4971 from 9rnsr/fix14973
Browse files Browse the repository at this point in the history
[REG2.068] Issue 14973 - compiler inference of contexts for nested map seems broken
  • Loading branch information
MartinNowak committed Aug 28, 2015
2 parents 45c115b + e891688 commit 955eb73
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 37 deletions.
54 changes: 37 additions & 17 deletions src/declaration.c
Expand Up @@ -1806,16 +1806,46 @@ bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("VarDeclaration::checkNestedReference() %s\n", toChars());
if (parent && parent != sc->parent &&
!isDataseg() && !(storage_class & STCmanifest) &&
sc->intypeof != 1 && !(sc->flags & SCOPEctfe))
if (sc->intypeof == 1 || (sc->flags & SCOPEctfe))
return false;
if (!parent || parent == sc->parent)
return false;
if (isDataseg() || (storage_class & STCmanifest))
return false;

// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (!fdthis)
return false; // out of function scope

Dsymbol *p = toParent2();

// Function literals from fdthis to p must be delegates
// TODO: here is similar to checkFrameAccess.
for (Dsymbol *s = fdthis; s && s != p; s = s->toParent2())
{
// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;

if (FuncDeclaration *fd = s->isFuncDeclaration())
{
if (!fd->isThis() && !fd->isNested())
break;
}
if (AggregateDeclaration *ad2 = s->isAggregateDeclaration())
{
if (ad2->storage_class & STCstatic)
break;
}
}

if (1)
{
// The function that this variable is in
FuncDeclaration *fdv = toParent()->isFuncDeclaration();
// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
FuncDeclaration *fdv = p->isFuncDeclaration();

if (fdv && fdthis && fdv != fdthis)
if (fdv && fdv != fdthis)
{
// Add fdthis to nestedrefs[] if not already there
for (size_t i = 0; 1; i++)
Expand Down Expand Up @@ -1875,16 +1905,6 @@ bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
}
}

// Function literals from fdthis to fdv must be delegates
for (Dsymbol *s = fdthis; s && s != fdv; s = s->toParent2())
{
// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
{
fld->tok = TOKdelegate;
}
}

// Add this to fdv->closureVars[] if not already there
for (size_t i = 0; 1; i++)
{
Expand Down
11 changes: 11 additions & 0 deletions src/expression.c
Expand Up @@ -3359,6 +3359,10 @@ Expression *DsymbolExp::semantic(Scope *sc)
v->semantic(v->scope);
s = v->toAlias(); // Need this if 'v' is a tuple variable
}

// Change the ancestor lambdas to delegate before hasThis(sc) call.
if (v->checkNestedReference(sc, loc))
return new ErrorExp();
}
if (s->needThis() && hasThis(sc))
{
Expand Down Expand Up @@ -5310,6 +5314,9 @@ Expression *VarExp::semantic(Scope *sc)
}
else if (FuncDeclaration *fd = var->isFuncDeclaration())
{
// TODO: If fd isn't yet resolved its overload, the checkNestedReference
// call would cause incorrect validation.
// Maybe here should be moved in CallExp, or AddrExp for functions.
if (fd->checkNestedReference(sc, loc))
return new ErrorExp();
}
Expand Down Expand Up @@ -8966,6 +8973,10 @@ Expression *CallExp::semantic(Scope *sc)

if (f->needThis())
{
// Change the ancestor lambdas to delegate before hasThis(sc) call.
if (f->checkNestedReference(sc, loc))
return new ErrorExp();

if (hasThis(sc))
{
// Supply an implicit 'this', as in
Expand Down
52 changes: 38 additions & 14 deletions src/func.c
Expand Up @@ -4120,20 +4120,50 @@ const char *FuncDeclaration::kind()
bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
if (parent && parent != sc->parent && this->isNested() &&
this->ident != Id::require && this->ident != Id::ensure)
if (!parent || parent == sc->parent)
return false;
if (ident == Id::require || ident == Id::ensure)
return false;
if (!isThis() && !isNested())
return false;

// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (!fdthis)
return false; // out of function scope

Dsymbol *p = toParent2();

// Function literals from fdthis to p must be delegates
// TODO: here is similar to checkFrameAccess.
for (Dsymbol *s = fdthis; s && s != p; s = s->toParent2())
{
// The function that this function is in
FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration();
// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;

if (FuncDeclaration *fd = s->isFuncDeclaration())
{
if (!fd->isThis() && !fd->isNested())
break;
}
if (AggregateDeclaration *ad2 = s->isAggregateDeclaration())
{
if (ad2->storage_class & STCstatic)
break;
}
}

// The current function
FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
if (isNested())
{
// The function that this function is in
FuncDeclaration *fdv2 = p->isFuncDeclaration();

//printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars());
//printf("fdv2 = %s in [%s]\n", fdv2->toChars(), fdv2->loc.toChars());
//printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars());

if (fdv2 && fdthis && fdv2 != fdthis)
if (fdv2 && fdv2 != fdthis)
{
// Add this function to the list of those which called us
if (fdthis != this)
Expand All @@ -4153,7 +4183,7 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
}
}

FuncDeclaration *fdv = toParent2()->isFuncDeclaration();
FuncDeclaration *fdv = p->isFuncDeclaration();
if (fdv && fdthis && fdv != fdthis)
{
int lv = fdthis->getLevel(loc, sc, fdv);
Expand All @@ -4165,12 +4195,6 @@ bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
return false; // same level call

// Uplevel call

// BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does

// function literal has reference to enclosing scope is delegate
if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;
}
}
return false;
Expand Down
54 changes: 54 additions & 0 deletions test/compilable/test14973.d
@@ -0,0 +1,54 @@
template map(fun...)
{
auto map(R)(R r)
{
return MapResult!(fun, R)(r);
}
}

struct MapResult(alias fun, R)
{
R _input;

@property bool empty() { return _input.length == 0; }
@property auto front() { return fun(_input[0]); }
void popFront() { _input = _input[1..$]; }
}

class Foo
{
int baz() { return 1; }
void bar()
{
auto s = [1].map!(i => baz()); // compiles
auto r = [1].map!(
// lambda1
i =>
[1].map!(
// lambda2
j =>
baz()
)
); // compiles <- error
}
}

class Bar
{
int baz;
void bar()
{
auto s = [1].map!(i => baz); // compiles
auto r = [1].map!(
// lambda1
i =>
[1].map!(
// lambda2
j =>
baz
)
); // compiles <- error
}
}

void main() {}
2 changes: 1 addition & 1 deletion test/fail_compilation/diag9831.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/diag9831.d(12): Error: function diag9831.main.__lambda1 cannot access frame of function D main
fail_compilation/diag9831.d(12): Error: cannot match delegate literal to function pointer type 'int function(int x)'
---
*/

Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/fail11545.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail11545.d(14): Error: need 'this' for 'x' of type 'int'
fail_compilation/fail11545.d(18): Error: need 'this' for 'x' of type 'int'
fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function()
fail_compilation/fail11545.d(17): Error: cannot implicitly convert expression (__lambda5) of type int delegate() pure nothrow @nogc @safe to int function()
---
*/

Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/fail120.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail120.d(12): Error: need 'this' for 'nodes' of type 'int[2]'
fail_compilation/fail120.d(13): Error: need 'this' for 'nodes' of type 'int[2]'
fail_compilation/fail120.d(12): Error: non-constant nested delegate literal expression __lambda4
fail_compilation/fail120.d(13): Error: non-constant nested delegate literal expression __lambda5
---
*/

Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/fail39.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail39.d(11): Error: function fail39.main.__funcliteral2 cannot access frame of function D main
fail_compilation/fail39.d(11): Error: function fail39.main.foo is a nested function and cannot be accessed from fail39.main.__funcliteral2
---
*/

Expand Down

0 comments on commit 955eb73

Please sign in to comment.