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