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