Daily bump.
[official-gcc.git] / gcc / d / dmd / nogc.d
blob9e45e4549ffb6dbd7e5a94a8511a081f0ecd173d
1 /**
2 * Checks that a function marked `@nogc` does not invoke the Garbage Collector.
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#nogc-functions, No-GC Functions)
6 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d, _nogc.d)
10 * Documentation: https://dlang.org/phobos/dmd_nogc.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nogc.d
14 module dmd.nogc;
16 import core.stdc.stdio;
18 import dmd.aggregate;
19 import dmd.astenums;
20 import dmd.declaration;
21 import dmd.dscope;
22 import dmd.dtemplate : isDsymbol;
23 import dmd.errors;
24 import dmd.expression;
25 import dmd.func;
26 import dmd.globals;
27 import dmd.init;
28 import dmd.mtype;
29 import dmd.postordervisitor;
30 import dmd.tokens;
31 import dmd.visitor;
33 /**************************************
34 * Look for GC-allocations
36 extern (C++) final class NOGCVisitor : StoppableVisitor
38 alias visit = typeof(super).visit;
39 public:
40 FuncDeclaration f;
41 bool checkOnly; // don't print errors
42 bool err;
44 extern (D) this(FuncDeclaration f) scope @safe
46 this.f = f;
49 void doCond(Expression exp)
51 if (exp)
52 walkPostorder(exp, this);
55 override void visit(Expression e)
59 override void visit(DeclarationExp e)
61 // Note that, walkPostorder does not support DeclarationExp today.
62 VarDeclaration v = e.declaration.isVarDeclaration();
63 if (v && !(v.storage_class & STC.manifest) && !v.isDataseg() && v._init)
65 if (ExpInitializer ei = v._init.isExpInitializer())
67 doCond(ei.exp);
72 /**
73 * Register that expression `e` requires the GC
74 * Params:
75 * e = expression that uses GC
76 * format = error message when `e` is used in a `@nogc` function.
77 * Must contain format strings "`@nogc` %s `%s`" referring to the function.
78 * Returns: `true` if `err` was set, `false` if it's not in a `@nogc` and not checkonly (-betterC)
80 private bool setGC(Expression e, const(char)* format)
82 if (checkOnly)
84 err = true;
85 return true;
87 if (f.setGC(e.loc, format))
89 error(e.loc, format, f.kind(), f.toPrettyChars());
90 err = true;
91 return true;
93 return false;
96 override void visit(CallExp e)
98 import dmd.id : Id;
99 import core.stdc.stdio : printf;
100 if (!e.f)
101 return;
103 // Treat lowered hook calls as their original expressions.
104 auto fd = stripHookTraceImpl(e.f);
105 if (fd.ident == Id._d_arraysetlengthT)
107 if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation"))
108 return;
109 f.printGCUsage(e.loc, "setting `length` may cause a GC allocation");
113 override void visit(ArrayLiteralExp e)
115 if (e.type.ty != Tarray || !e.elements || !e.elements.length || e.onstack)
116 return;
117 if (setGC(e, "array literal in `@nogc` %s `%s` may cause a GC allocation"))
118 return;
119 f.printGCUsage(e.loc, "array literal may cause a GC allocation");
122 override void visit(AssocArrayLiteralExp e)
124 if (!e.keys.length)
125 return;
126 if (setGC(e, "associative array literal in `@nogc` %s `%s` may cause a GC allocation"))
127 return;
128 f.printGCUsage(e.loc, "associative array literal may cause a GC allocation");
131 override void visit(NewExp e)
133 if (e.member && !e.member.isNogc() && f.setGC(e.loc, null))
135 // @nogc-ness is already checked in NewExp::semantic
136 return;
138 if (e.onstack)
139 return;
140 if (global.params.ehnogc && e.thrownew)
141 return; // separate allocator is called for this, not the GC
143 if (setGC(e, "cannot use `new` in `@nogc` %s `%s`"))
144 return;
145 f.printGCUsage(e.loc, "`new` causes a GC allocation");
148 override void visit(DeleteExp e)
150 if (VarExp ve = e.e1.isVarExp())
152 VarDeclaration v = ve.var.isVarDeclaration();
153 if (v && v.onstack)
154 return; // delete for scope allocated class object
157 // Semantic should have already handled this case.
158 assert(0);
161 override void visit(IndexExp e)
163 Type t1b = e.e1.type.toBasetype();
164 if (e.modifiable && t1b.ty == Taarray)
166 if (setGC(e, "assigning an associative array element in `@nogc` %s `%s` may cause a GC allocation"))
167 return;
168 f.printGCUsage(e.loc, "assigning an associative array element may cause a GC allocation");
172 override void visit(AssignExp e)
174 if (e.e1.op == EXP.arrayLength)
176 if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation"))
177 return;
178 f.printGCUsage(e.loc, "setting `length` may cause a GC allocation");
182 override void visit(CatAssignExp e)
184 if (checkOnly)
186 err = true;
187 return;
189 if (setGC(e, "cannot use operator `~=` in `@nogc` %s `%s`"))
190 return;
191 f.printGCUsage(e.loc, "operator `~=` may cause a GC allocation");
194 override void visit(CatExp e)
196 if (setGC(e, "cannot use operator `~` in `@nogc` %s `%s`"))
197 return;
198 f.printGCUsage(e.loc, "operator `~` may cause a GC allocation");
202 Expression checkGC(Scope* sc, Expression e)
204 if (sc.flags & SCOPE.ctfeBlock) // ignore GC in ctfe blocks
205 return e;
207 /* If betterC, allow GC to happen in non-CTFE code.
208 * Just don't generate code for it.
209 * Detect non-CTFE use of the GC in betterC code.
211 const betterC = !global.params.useGC;
212 FuncDeclaration f = sc.func;
213 if (e && e.op != EXP.error && f && sc.intypeof != 1 &&
214 (!(sc.flags & SCOPE.ctfe) || betterC) &&
215 (f.type.ty == Tfunction &&
216 (cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.v.gc) &&
217 !(sc.flags & SCOPE.debug_))
219 scope NOGCVisitor gcv = new NOGCVisitor(f);
220 gcv.checkOnly = betterC;
221 walkPostorder(e, gcv);
222 if (gcv.err)
224 if (betterC)
226 /* Allow ctfe to use the gc code, but don't let it into the runtime
228 f.skipCodegen = true;
230 else
231 return ErrorExp.get();
234 return e;
238 * Removes `_d_HookTraceImpl` if found from `fd`.
239 * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper.
240 * Parameters:
241 * fd = The function declaration to remove `_d_HookTraceImpl` from
243 private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
245 import dmd.id : Id;
246 import dmd.dsymbol : Dsymbol;
247 import dmd.rootobject : RootObject, DYNCAST;
249 if (fd.ident != Id._d_HookTraceImpl)
250 return fd;
252 // Get the Hook from the second template parameter
253 auto templateInstance = fd.parent.isTemplateInstance;
254 RootObject hook = (*templateInstance.tiargs)[1];
255 Dsymbol s = hook.isDsymbol();
256 assert(s, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!");
257 return s.isFuncDeclaration;