view gcc/d/dmd/delegatize.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
line wrap: on
line source


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