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 }