/* 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; }