1
2/* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/nogc.c
9 */
10
11#include "mars.h"
12#include "init.h"
13#include "visitor.h"
14#include "expression.h"
15#include "statement.h"
16#include "declaration.h"
17#include "id.h"
18#include "module.h"
19#include "scope.h"
20#include "tokens.h"
21#include "aggregate.h"
22
23bool walkPostorder(Expression *e, StoppableVisitor *v);
24
25void FuncDeclaration::printGCUsage(Loc loc, const char* warn)
26{
27    if (!global.params.vgc)
28        return;
29
30    Module *m = getModule();
31    if (m && m->isRoot() && !inUnittest())
32    {
33        message(loc, "vgc: %s", warn);
34    }
35}
36
37/**************************************
38 * Look for GC-allocations
39 */
40class NOGCVisitor : public StoppableVisitor
41{
42public:
43    FuncDeclaration *f;
44    bool err;
45
46    NOGCVisitor(FuncDeclaration *f)
47    {
48        this->f = f;
49        this->err = false;
50    }
51
52    void doCond(Expression *exp)
53    {
54        if (exp)
55            walkPostorder(exp, this);
56    }
57
58    void visit(Expression *)
59    {
60    }
61
62    void visit(DeclarationExp *e)
63    {
64        // Note that, walkPostorder does not support DeclarationExp today.
65        VarDeclaration *v = e->declaration->isVarDeclaration();
66        if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->_init)
67        {
68            if (ExpInitializer *ei = v->_init->isExpInitializer())
69            {
70                doCond(ei->exp);
71            }
72        }
73    }
74
75    void visit(CallExp *)
76    {
77    }
78
79    void visit(ArrayLiteralExp *e)
80    {
81        if (e->type->ty != Tarray || !e->elements || !e->elements->dim)
82            return;
83
84        if (f->setGC())
85        {
86            e->error("array literal in @nogc %s '%s' may cause GC allocation",
87                f->kind(), f->toPrettyChars());
88            err = true;
89            return;
90        }
91        f->printGCUsage(e->loc, "array literal may cause GC allocation");
92    }
93
94    void visit(AssocArrayLiteralExp *e)
95    {
96        if (!e->keys->dim)
97            return;
98
99        if (f->setGC())
100        {
101            e->error("associative array literal in @nogc %s '%s' may cause GC allocation",
102                f->kind(), f->toPrettyChars());
103            err = true;
104            return;
105        }
106        f->printGCUsage(e->loc, "associative array literal may cause GC allocation");
107    }
108
109    void visit(NewExp *e)
110    {
111        if (e->member && !e->member->isNogc() && f->setGC())
112        {
113            // @nogc-ness is already checked in NewExp::semantic
114            return;
115        }
116        if (e->onstack)
117            return;
118        if (e->allocator)
119            return;
120
121        if (f->setGC())
122        {
123            e->error("cannot use 'new' in @nogc %s '%s'",
124                f->kind(), f->toPrettyChars());
125            err = true;
126            return;
127        }
128        f->printGCUsage(e->loc, "'new' causes GC allocation");
129    }
130
131    void visit(DeleteExp *e)
132    {
133        if (e->e1->op == TOKvar)
134        {
135            VarDeclaration *v =  ((VarExp *)e->e1)->var->isVarDeclaration();
136            if (v && v->onstack)
137                return;     // delete for scope allocated class object
138        }
139
140        Type *tb = e->e1->type->toBasetype();
141        AggregateDeclaration *ad = NULL;
142        switch (tb->ty)
143        {
144        case Tclass:
145            ad = ((TypeClass *)tb)->sym;
146            break;
147
148        case Tpointer:
149            tb = ((TypePointer *)tb)->next->toBasetype();
150            if (tb->ty == Tstruct)
151                ad = ((TypeStruct *)tb)->sym;
152            break;
153
154        default:
155            break;
156        }
157        if (ad && ad->aggDelete)
158            return;
159
160        if (f->setGC())
161        {
162            e->error("cannot use 'delete' in @nogc %s '%s'",
163                f->kind(), f->toPrettyChars());
164            err = true;
165            return;
166        }
167        f->printGCUsage(e->loc, "'delete' requires GC");
168    }
169
170    void visit(IndexExp* e)
171    {
172        Type *t1b = e->e1->type->toBasetype();
173        if (t1b->ty == Taarray)
174        {
175            if (f->setGC())
176            {
177                e->error("indexing an associative array in @nogc %s '%s' may cause GC allocation",
178                    f->kind(), f->toPrettyChars());
179                err = true;
180                return;
181            }
182            f->printGCUsage(e->loc, "indexing an associative array may cause GC allocation");
183        }
184    }
185
186    void visit(AssignExp *e)
187    {
188        if (e->e1->op == TOKarraylength)
189        {
190            if (f->setGC())
191            {
192                e->error("setting 'length' in @nogc %s '%s' may cause GC allocation",
193                    f->kind(), f->toPrettyChars());
194                err = true;
195                return;
196            }
197            f->printGCUsage(e->loc, "setting 'length' may cause GC allocation");
198        }
199    }
200
201    void visit(CatAssignExp *e)
202    {
203        if (f->setGC())
204        {
205            e->error("cannot use operator ~= in @nogc %s '%s'",
206                f->kind(), f->toPrettyChars());
207            err = true;
208            return;
209        }
210        f->printGCUsage(e->loc, "operator ~= may cause GC allocation");
211    }
212
213    void visit(CatExp *e)
214    {
215        if (f->setGC())
216        {
217            e->error("cannot use operator ~ in @nogc %s '%s'",
218                f->kind(), f->toPrettyChars());
219            err = true;
220            return;
221        }
222        f->printGCUsage(e->loc, "operator ~ may cause GC allocation");
223    }
224};
225
226Expression *checkGC(Scope *sc, Expression *e)
227{
228    FuncDeclaration *f = sc->func;
229    if (e && e->op != TOKerror &&
230        f && sc->intypeof != 1 && !(sc->flags & SCOPEctfe) &&
231        ((f->type->ty == Tfunction && ((TypeFunction *)f->type)->isnogc) ||
232         (f->flags & FUNCFLAGnogcInprocess) ||
233         global.params.vgc))
234    {
235        NOGCVisitor gcv(f);
236        walkPostorder(e, &gcv);
237        if (gcv.err)
238            return new ErrorExp();
239    }
240    return e;
241}
242