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 
23 bool walkPostorder(Expression *e, StoppableVisitor *v);
24 
printGCUsage(Loc loc,const char * warn)25 void 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  */
40 class NOGCVisitor : public StoppableVisitor
41 {
42 public:
43     FuncDeclaration *f;
44     bool err;
45 
NOGCVisitor(FuncDeclaration * f)46     NOGCVisitor(FuncDeclaration *f)
47     {
48         this->f = f;
49         this->err = false;
50     }
51 
doCond(Expression * exp)52     void doCond(Expression *exp)
53     {
54         if (exp)
55             walkPostorder(exp, this);
56     }
57 
visit(Expression *)58     void visit(Expression *)
59     {
60     }
61 
visit(DeclarationExp * e)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 
visit(CallExp *)75     void visit(CallExp *)
76     {
77     }
78 
visit(ArrayLiteralExp * e)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 
visit(AssocArrayLiteralExp * e)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 
visit(NewExp * e)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 
visit(DeleteExp * e)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 
visit(IndexExp * e)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 
visit(AssignExp * e)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 
visit(CatAssignExp * e)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 
visit(CatExp * e)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 
checkGC(Scope * sc,Expression * e)226 Expression *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