/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/nogc.c */ #include "mars.h" #include "init.h" #include "visitor.h" #include "expression.h" #include "statement.h" #include "declaration.h" #include "id.h" #include "module.h" #include "scope.h" #include "tokens.h" #include "aggregate.h" bool walkPostorder(Expression *e, StoppableVisitor *v); void FuncDeclaration::printGCUsage(Loc loc, const char* warn) { if (!global.params.vgc) return; Module *m = getModule(); if (m && m->isRoot() && !inUnittest()) { message(loc, "vgc: %s", warn); } } /************************************** * Look for GC-allocations */ class NOGCVisitor : public StoppableVisitor { public: FuncDeclaration *f; bool err; NOGCVisitor(FuncDeclaration *f) { this->f = f; this->err = false; } void doCond(Expression *exp) { if (exp) walkPostorder(exp, this); } void visit(Expression *) { } void visit(DeclarationExp *e) { // Note that, walkPostorder does not support DeclarationExp today. VarDeclaration *v = e->declaration->isVarDeclaration(); if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->_init) { if (ExpInitializer *ei = v->_init->isExpInitializer()) { doCond(ei->exp); } } } void visit(CallExp *) { } void visit(ArrayLiteralExp *e) { if (e->type->ty != Tarray || !e->elements || !e->elements->dim) return; if (f->setGC()) { e->error("array literal in @nogc %s '%s' may cause GC allocation", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "array literal may cause GC allocation"); } void visit(AssocArrayLiteralExp *e) { if (!e->keys->dim) return; if (f->setGC()) { e->error("associative array literal in @nogc %s '%s' may cause GC allocation", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "associative array literal may cause GC allocation"); } void visit(NewExp *e) { if (e->member && !e->member->isNogc() && f->setGC()) { // @nogc-ness is already checked in NewExp::semantic return; } if (e->onstack) return; if (e->allocator) return; if (f->setGC()) { e->error("cannot use 'new' in @nogc %s '%s'", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "'new' causes GC allocation"); } void visit(DeleteExp *e) { if (e->e1->op == TOKvar) { VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration(); if (v && v->onstack) return; // delete for scope allocated class object } Type *tb = e->e1->type->toBasetype(); AggregateDeclaration *ad = NULL; switch (tb->ty) { case Tclass: ad = ((TypeClass *)tb)->sym; break; case Tpointer: tb = ((TypePointer *)tb)->next->toBasetype(); if (tb->ty == Tstruct) ad = ((TypeStruct *)tb)->sym; break; default: break; } if (ad && ad->aggDelete) return; if (f->setGC()) { e->error("cannot use 'delete' in @nogc %s '%s'", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "'delete' requires GC"); } void visit(IndexExp* e) { Type *t1b = e->e1->type->toBasetype(); if (t1b->ty == Taarray) { if (f->setGC()) { e->error("indexing an associative array in @nogc %s '%s' may cause GC allocation", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "indexing an associative array may cause GC allocation"); } } void visit(AssignExp *e) { if (e->e1->op == TOKarraylength) { if (f->setGC()) { e->error("setting 'length' in @nogc %s '%s' may cause GC allocation", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "setting 'length' may cause GC allocation"); } } void visit(CatAssignExp *e) { if (f->setGC()) { e->error("cannot use operator ~= in @nogc %s '%s'", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "operator ~= may cause GC allocation"); } void visit(CatExp *e) { if (f->setGC()) { e->error("cannot use operator ~ in @nogc %s '%s'", f->kind(), f->toPrettyChars()); err = true; return; } f->printGCUsage(e->loc, "operator ~ may cause GC allocation"); } }; Expression *checkGC(Scope *sc, Expression *e) { FuncDeclaration *f = sc->func; if (e && e->op != TOKerror && f && sc->intypeof != 1 && !(sc->flags & SCOPEctfe) && ((f->type->ty == Tfunction && ((TypeFunction *)f->type)->isnogc) || (f->flags & FUNCFLAGnogcInprocess) || global.params.vgc)) { NOGCVisitor gcv(f); walkPostorder(e, &gcv); if (gcv.err) return new ErrorExp(); } return e; }