blob: 6e4d3d9cd8d8479a890cad35be3af69e81727302 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/delegatize.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "expression.h"
#include "statement.h"
#include "mtype.h"
#include "utf.h"
#include "declaration.h"
#include "aggregate.h"
#include "scope.h"
#include "init.h"
#include "tokens.h"
bool walkPostorder(Expression *e, StoppableVisitor *v);
void lambdaSetParent(Expression *e, Scope *sc);
bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
Expression *semantic(Expression *e, Scope *sc);
/********************************************
* Convert from expression to delegate that returns the expression,
* i.e. convert:
* expr
* to:
* typeof(expr) delegate() { return expr; }
*/
Expression *toDelegate(Expression *e, Type* t, Scope *sc)
{
//printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), e->toChars());
Loc loc = e->loc;
TypeFunction *tf = new TypeFunction(NULL, t, 0, LINKd);
if (t->hasWild())
tf->mod = MODwild;
FuncLiteralDeclaration *fld =
new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL);
sc = sc->push();
sc->parent = fld; // set current function to be the delegate
lambdaSetParent(e, sc);
bool r = lambdaCheckForNestedRef(e, sc);
sc = sc->pop();
if (r)
return new ErrorExp();
Statement *s;
if (t->ty == Tvoid)
s = new ExpStatement(loc, e);
else
s = new ReturnStatement(loc, e);
fld->fbody = s;
e = new FuncExp(loc, fld);
e = semantic(e, sc);
return e;
}
/******************************************
* Patch the parent of declarations to be the new function literal.
*/
void lambdaSetParent(Expression *e, Scope *sc)
{
class LambdaSetParent : public StoppableVisitor
{
Scope *sc;
public:
LambdaSetParent(Scope *sc) : sc(sc) {}
void visit(Expression *)
{
}
void visit(DeclarationExp *e)
{
e->declaration->parent = sc->parent;
}
void visit(IndexExp *e)
{
if (e->lengthVar)
{
//printf("lengthVar\n");
e->lengthVar->parent = sc->parent;
}
}
void visit(SliceExp *e)
{
if (e->lengthVar)
{
//printf("lengthVar\n");
e->lengthVar->parent = sc->parent;
}
}
};
LambdaSetParent lsp(sc);
walkPostorder(e, &lsp);
}
/*******************************************
* Look for references to variables in a scope enclosing the new function literal.
* Returns true if error occurs.
*/
bool lambdaCheckForNestedRef(Expression *e, Scope *sc)
{
class LambdaCheckForNestedRef : public StoppableVisitor
{
public:
Scope *sc;
bool result;
LambdaCheckForNestedRef(Scope *sc)
: sc(sc), result(false)
{
}
void visit(Expression *)
{
}
void visit(SymOffExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
result = v->checkNestedReference(sc, Loc());
}
void visit(VarExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
result = v->checkNestedReference(sc, Loc());
}
void visit(ThisExp *e)
{
if (e->var)
result = e->var->checkNestedReference(sc, Loc());
}
void visit(DeclarationExp *e)
{
VarDeclaration *v = e->declaration->isVarDeclaration();
if (v)
{
result = v->checkNestedReference(sc, Loc());
if (result)
return;
/* Some expressions cause the frontend to create a temporary.
* For example, structs with cpctors replace the original
* expression e with:
* __cpcttmp = __cpcttmp.cpctor(e);
*
* In this instance, we need to ensure that the original
* expression e does not have any nested references by
* checking the declaration initializer too.
*/
if (v->_init && v->_init->isExpInitializer())
{
Expression *ie = initializerToExpression(v->_init);
result = lambdaCheckForNestedRef(ie, sc);
}
}
}
};
LambdaCheckForNestedRef v(sc);
walkPostorder(e, &v);
return v.result;
}
bool checkNestedRef(Dsymbol *s, Dsymbol *p)
{
while (s)
{
if (s == p) // hit!
return false;
if (FuncDeclaration *fd = s->isFuncDeclaration())
{
if (!fd->isThis() && !fd->isNested())
break;
// Bugzilla 15332: change to delegate if fd is actually nested.
if (FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;
}
if (AggregateDeclaration *ad = s->isAggregateDeclaration())
{
if (ad->storage_class & STCstatic)
break;
}
s = s->toParent2();
}
return true;
}