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 23bool walkPostorder(Expression *e, StoppableVisitor *v); 24 25void 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 */ 40class NOGCVisitor : public StoppableVisitor 41{ 42public: 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 { 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 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 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 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 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 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 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 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 226Expression *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