]>
Commit | Line | Data |
---|---|---|
b4c522fa IB |
1 | |
2 | /* Compiler implementation of the D programming language | |
a3b38b77 | 3 | * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved |
b4c522fa IB |
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 | ||
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 | ||
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 | { | |
2cbc99d1 | 81 | if (e->type->ty != Tarray || !e->elements || !e->elements->length) |
b4c522fa IB |
82 | return; |
83 | ||
84 | if (f->setGC()) | |
85 | { | |
a3b38b77 | 86 | e->error("array literal in @nogc %s `%s` may cause GC allocation", |
b4c522fa IB |
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 | { | |
2cbc99d1 | 96 | if (!e->keys->length) |
b4c522fa IB |
97 | return; |
98 | ||
99 | if (f->setGC()) | |
100 | { | |
a3b38b77 | 101 | e->error("associative array literal in @nogc %s `%s` may cause GC allocation", |
b4c522fa IB |
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 | { | |
a3b38b77 | 123 | e->error("cannot use `new` in @nogc %s `%s`", |
b4c522fa IB |
124 | f->kind(), f->toPrettyChars()); |
125 | err = true; | |
126 | return; | |
127 | } | |
a3b38b77 | 128 | f->printGCUsage(e->loc, "`new` causes GC allocation"); |
b4c522fa IB |
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 | { | |
a3b38b77 | 162 | e->error("cannot use `delete` in @nogc %s `%s`", |
b4c522fa IB |
163 | f->kind(), f->toPrettyChars()); |
164 | err = true; | |
165 | return; | |
166 | } | |
a3b38b77 | 167 | f->printGCUsage(e->loc, "`delete` requires GC"); |
b4c522fa IB |
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 | { | |
a3b38b77 | 177 | e->error("indexing an associative array in @nogc %s `%s` may cause GC allocation", |
b4c522fa IB |
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 | { | |
a3b38b77 | 192 | e->error("setting `length` in @nogc %s `%s` may cause GC allocation", |
b4c522fa IB |
193 | f->kind(), f->toPrettyChars()); |
194 | err = true; | |
195 | return; | |
196 | } | |
a3b38b77 | 197 | f->printGCUsage(e->loc, "setting `length` may cause GC allocation"); |
b4c522fa IB |
198 | } |
199 | } | |
200 | ||
201 | void visit(CatAssignExp *e) | |
202 | { | |
203 | if (f->setGC()) | |
204 | { | |
a3b38b77 | 205 | e->error("cannot use operator ~= in @nogc %s `%s`", |
b4c522fa IB |
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 | { | |
a3b38b77 | 217 | e->error("cannot use operator ~ in @nogc %s `%s`", |
b4c522fa IB |
218 | f->kind(), f->toPrettyChars()); |
219 | err = true; | |
220 | return; | |
221 | } | |
222 | f->printGCUsage(e->loc, "operator ~ may cause GC allocation"); | |
223 | } | |
224 | }; | |
225 | ||
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 | } |