]>
Commit | Line | Data |
---|---|---|
5fee5ec3 IB |
1 | /** |
2 | * Defines a function declaration. | |
3 | * | |
4 | * Includes: | |
5 | * - function/delegate literals | |
6 | * - function aliases | |
7 | * - (static/shared) constructors/destructors/post-blits | |
8 | * - `invariant` | |
9 | * - `unittest` | |
10 | * | |
c43b5909 IB |
11 | * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
12 | * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
13 | * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
5fee5ec3 IB |
14 | * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/func.d, _func.d) |
15 | * Documentation: https://dlang.org/phobos/dmd_func.html | |
16 | * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/func.d | |
17 | */ | |
18 | ||
19 | module dmd.func; | |
20 | ||
21 | import core.stdc.stdio; | |
22 | import core.stdc.string; | |
23 | import dmd.aggregate; | |
24 | import dmd.arraytypes; | |
25 | import dmd.astenums; | |
26 | import dmd.blockexit; | |
27 | import dmd.gluelayer; | |
28 | import dmd.dclass; | |
29 | import dmd.declaration; | |
30 | import dmd.delegatize; | |
31 | import dmd.dinterpret; | |
32 | import dmd.dmodule; | |
33 | import dmd.dscope; | |
34 | import dmd.dstruct; | |
35 | import dmd.dsymbol; | |
36 | import dmd.dsymbolsem; | |
37 | import dmd.dtemplate; | |
38 | import dmd.errors; | |
39 | import dmd.escape; | |
40 | import dmd.expression; | |
41 | import dmd.globals; | |
42 | import dmd.hdrgen; | |
43 | import dmd.id; | |
44 | import dmd.identifier; | |
45 | import dmd.init; | |
46 | import dmd.mtype; | |
47 | import dmd.objc; | |
0fb57034 IB |
48 | import dmd.root.aav; |
49 | import dmd.common.outbuffer; | |
5fee5ec3 IB |
50 | import dmd.root.rootobject; |
51 | import dmd.root.string; | |
52 | import dmd.root.stringtable; | |
53 | import dmd.semantic2; | |
54 | import dmd.semantic3; | |
55 | import dmd.statement_rewrite_walker; | |
56 | import dmd.statement; | |
57 | import dmd.statementsem; | |
58 | import dmd.tokens; | |
59 | import dmd.visitor; | |
60 | ||
61 | /// Inline Status | |
62 | enum ILS : ubyte | |
63 | { | |
64 | uninitialized, /// not computed yet | |
65 | no, /// cannot inline | |
66 | yes, /// can inline | |
67 | } | |
68 | ||
69 | enum BUILTIN : ubyte | |
70 | { | |
71 | unknown = 255, /// not known if this is a builtin | |
72 | unimp = 0, /// this is not a builtin | |
73 | gcc, /// this is a GCC builtin | |
74 | llvm, /// this is an LLVM builtin | |
75 | sin, | |
76 | cos, | |
77 | tan, | |
78 | sqrt, | |
79 | fabs, | |
80 | ldexp, | |
81 | log, | |
82 | log2, | |
83 | log10, | |
84 | exp, | |
85 | expm1, | |
86 | exp2, | |
87 | round, | |
88 | floor, | |
89 | ceil, | |
90 | trunc, | |
91 | copysign, | |
92 | pow, | |
93 | fmin, | |
94 | fmax, | |
95 | fma, | |
96 | isnan, | |
97 | isinfinity, | |
98 | isfinite, | |
99 | bsf, | |
100 | bsr, | |
101 | bswap, | |
102 | popcnt, | |
103 | yl2x, | |
104 | yl2xp1, | |
105 | toPrecFloat, | |
106 | toPrecDouble, | |
107 | toPrecReal | |
108 | } | |
109 | ||
110 | /* Tweak all return statements and dtor call for nrvo_var, for correct NRVO. | |
111 | */ | |
112 | extern (C++) final class NrvoWalker : StatementRewriteWalker | |
113 | { | |
114 | alias visit = typeof(super).visit; | |
115 | public: | |
116 | FuncDeclaration fd; | |
117 | Scope* sc; | |
118 | ||
119 | override void visit(ReturnStatement s) | |
120 | { | |
121 | // See if all returns are instead to be replaced with a goto returnLabel; | |
122 | if (fd.returnLabel) | |
123 | { | |
124 | /* Rewrite: | |
125 | * return exp; | |
126 | * as: | |
127 | * vresult = exp; goto Lresult; | |
128 | */ | |
129 | auto gs = new GotoStatement(s.loc, Id.returnLabel); | |
130 | gs.label = fd.returnLabel; | |
131 | ||
132 | Statement s1 = gs; | |
133 | if (s.exp) | |
134 | s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs); | |
135 | ||
136 | replaceCurrent(s1); | |
137 | } | |
138 | } | |
139 | ||
140 | override void visit(TryFinallyStatement s) | |
141 | { | |
142 | DtorExpStatement des; | |
235d5a96 | 143 | if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null && |
5fee5ec3 IB |
144 | fd.nrvo_var == des.var) |
145 | { | |
146 | if (!(global.params.useExceptions && ClassDeclaration.throwable)) | |
147 | { | |
148 | /* Don't need to call destructor at all, since it is nrvo | |
149 | */ | |
150 | replaceCurrent(s._body); | |
151 | s._body.accept(this); | |
152 | return; | |
153 | } | |
154 | ||
155 | /* Normally local variable dtors are called regardless exceptions. | |
156 | * But for nrvo_var, its dtor should be called only when exception is thrown. | |
157 | * | |
158 | * Rewrite: | |
159 | * try { s.body; } finally { nrvo_var.edtor; } | |
160 | * // equivalent with: | |
161 | * // s.body; scope(exit) nrvo_var.edtor; | |
162 | * as: | |
163 | * try { s.body; } catch(Throwable __o) { nrvo_var.edtor; throw __o; } | |
164 | * // equivalent with: | |
165 | * // s.body; scope(failure) nrvo_var.edtor; | |
166 | */ | |
167 | Statement sexception = new DtorExpStatement(Loc.initial, fd.nrvo_var.edtor, fd.nrvo_var); | |
168 | Identifier id = Identifier.generateId("__o"); | |
169 | ||
170 | Statement handler = new PeelStatement(sexception); | |
171 | if (sexception.blockExit(fd, false) & BE.fallthru) | |
172 | { | |
173 | auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id)); | |
174 | ts.internalThrow = true; | |
175 | handler = new CompoundStatement(Loc.initial, handler, ts); | |
176 | } | |
177 | ||
178 | auto catches = new Catches(); | |
179 | auto ctch = new Catch(Loc.initial, getThrowable(), id, handler); | |
180 | ctch.internalCatch = true; | |
181 | ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o' | |
182 | catches.push(ctch); | |
183 | ||
184 | Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches); | |
7e7ebe3e | 185 | fd.hasNoEH = false; |
5fee5ec3 IB |
186 | replaceCurrent(s2); |
187 | s2.accept(this); | |
188 | } | |
189 | else | |
190 | StatementRewriteWalker.visit(s); | |
191 | } | |
192 | } | |
193 | ||
7e7ebe3e | 194 | private struct FUNCFLAG |
5fee5ec3 | 195 | { |
7e7ebe3e IB |
196 | bool purityInprocess; /// working on determining purity |
197 | bool safetyInprocess; /// working on determining safety | |
198 | bool nothrowInprocess; /// working on determining nothrow | |
199 | bool nogcInprocess; /// working on determining @nogc | |
200 | bool returnInprocess; /// working on inferring 'return' for parameters | |
201 | bool inlineScanned; /// function has been scanned for inline possibilities | |
202 | bool inferScope; /// infer 'scope' for parameters | |
203 | bool hasCatches; /// function has try-catch statements | |
204 | bool isCompileTimeOnly; /// is a compile time only function; no code will be generated for it | |
205 | bool printf; /// is a printf-like function | |
206 | bool scanf; /// is a scanf-like function | |
207 | bool noreturn; /// the function does not return | |
208 | bool isNRVO = true; /// Support for named return value optimization | |
209 | bool isNaked; /// The function is 'naked' (see inline ASM) | |
210 | bool isGenerated; /// The function is compiler generated (e.g. `opCmp`) | |
211 | bool isIntroducing; /// If this function introduces the overload set | |
212 | bool hasSemantic3Errors; /// If errors in semantic3 this function's frame ptr | |
213 | bool hasNoEH; /// No exception unwinding is needed | |
214 | bool inferRetType; /// Return type is to be inferred | |
215 | bool hasDualContext; /// has a dual-context 'this' parameter | |
216 | bool hasAlwaysInlines; /// Contains references to functions that must be inlined | |
217 | bool isCrtCtor; /// Has attribute pragma(crt_constructor) | |
218 | bool isCrtDtor; /// Has attribute pragma(crt_destructor) | |
5fee5ec3 IB |
219 | } |
220 | ||
221 | /*********************************************************** | |
222 | * Tuple of result identifier (possibly null) and statement. | |
223 | * This is used to store out contracts: out(id){ ensure } | |
224 | */ | |
225 | extern (C++) struct Ensure | |
226 | { | |
227 | Identifier id; | |
228 | Statement ensure; | |
229 | ||
230 | Ensure syntaxCopy() | |
231 | { | |
232 | return Ensure(id, ensure.syntaxCopy()); | |
233 | } | |
234 | ||
235 | /***************************************** | |
236 | * Do syntax copy of an array of Ensure's. | |
237 | */ | |
238 | static Ensures* arraySyntaxCopy(Ensures* a) | |
239 | { | |
240 | Ensures* b = null; | |
241 | if (a) | |
242 | { | |
243 | b = a.copy(); | |
244 | foreach (i, e; *a) | |
245 | { | |
246 | (*b)[i] = e.syntaxCopy(); | |
247 | } | |
248 | } | |
249 | return b; | |
250 | } | |
251 | ||
252 | } | |
253 | ||
254 | /*********************************************************** | |
255 | */ | |
256 | extern (C++) class FuncDeclaration : Declaration | |
257 | { | |
258 | Statements* frequires; /// in contracts | |
259 | Ensures* fensures; /// out contracts | |
260 | Statement frequire; /// lowered in contract | |
261 | Statement fensure; /// lowered out contract | |
262 | Statement fbody; /// function body | |
263 | ||
264 | FuncDeclarations foverrides; /// functions this function overrides | |
265 | FuncDeclaration fdrequire; /// function that does the in contract | |
266 | FuncDeclaration fdensure; /// function that does the out contract | |
267 | ||
268 | Expressions* fdrequireParams; /// argument list for __require | |
269 | Expressions* fdensureParams; /// argument list for __ensure | |
270 | ||
271 | const(char)* mangleString; /// mangled symbol created from mangleExact() | |
272 | ||
273 | VarDeclaration vresult; /// result variable for out contracts | |
274 | LabelDsymbol returnLabel; /// where the return goes | |
275 | ||
0fb57034 IB |
276 | bool[size_t] isTypeIsolatedCache; /// cache for the potentially very expensive isTypeIsolated check |
277 | ||
5fee5ec3 IB |
278 | // used to prevent symbols in different |
279 | // scopes from having the same name | |
280 | DsymbolTable localsymtab; | |
281 | VarDeclaration vthis; /// 'this' parameter (member and nested) | |
5fee5ec3 IB |
282 | VarDeclaration v_arguments; /// '_arguments' parameter |
283 | ||
284 | VarDeclaration v_argptr; /// '_argptr' variable | |
285 | VarDeclarations* parameters; /// Array of VarDeclaration's for parameters | |
286 | DsymbolTable labtab; /// statement label symbol table | |
287 | Dsymbol overnext; /// next in overload list | |
288 | FuncDeclaration overnext0; /// next in overload list (only used during IFTI) | |
289 | Loc endloc; /// location of closing curly bracket | |
290 | int vtblIndex = -1; /// for member functions, index into vtbl[] | |
5fee5ec3 IB |
291 | |
292 | ILS inlineStatusStmt = ILS.uninitialized; | |
293 | ILS inlineStatusExp = ILS.uninitialized; | |
294 | PINLINE inlining = PINLINE.default_; | |
295 | ||
296 | int inlineNest; /// !=0 if nested inline | |
5fee5ec3 | 297 | |
5fee5ec3 IB |
298 | ForeachStatement fes; /// if foreach body, this is the foreach |
299 | BaseClass* interfaceVirtual; /// if virtual, but only appears in base interface vtbl[] | |
5fee5ec3 IB |
300 | /** if !=NULL, then this is the type |
301 | of the 'introducing' function | |
302 | this one is overriding | |
303 | */ | |
304 | Type tintro; | |
305 | ||
5fee5ec3 IB |
306 | StorageClass storage_class2; /// storage class for template onemember's |
307 | ||
308 | // Things that should really go into Scope | |
309 | ||
310 | /// 1 if there's a return exp; statement | |
311 | /// 2 if there's a throw statement | |
312 | /// 4 if there's an assert(0) | |
313 | /// 8 if there's inline asm | |
314 | /// 16 if there are multiple return statements | |
315 | int hasReturnExp; | |
316 | ||
5fee5ec3 IB |
317 | VarDeclaration nrvo_var; /// variable to replace with shidden |
318 | Symbol* shidden; /// hidden pointer passed to function | |
319 | ||
320 | ReturnStatements* returns; | |
321 | ||
322 | GotoStatements* gotos; /// Gotos with forward references | |
323 | ||
324 | /// set if this is a known, builtin function we can evaluate at compile time | |
325 | BUILTIN builtin = BUILTIN.unknown; | |
326 | ||
327 | /// set if someone took the address of this function | |
328 | int tookAddressOf; | |
329 | ||
330 | bool requiresClosure; // this function needs a closure | |
331 | ||
332 | /** local variables in this function which are referenced by nested functions | |
333 | * (They'll get put into the "closure" for this function.) | |
334 | */ | |
335 | VarDeclarations closureVars; | |
336 | ||
337 | /** Outer variables which are referenced by this nested function | |
338 | * (the inverse of closureVars) | |
339 | */ | |
340 | VarDeclarations outerVars; | |
341 | ||
342 | /// Sibling nested functions which called this one | |
343 | FuncDeclarations siblingCallers; | |
344 | ||
345 | FuncDeclarations *inlinedNestedCallees; | |
346 | ||
5eb9927a IB |
347 | /// In case of failed `@safe` inference, store the error that made the function `@system` for |
348 | /// better diagnostics | |
610d7898 | 349 | AttributeViolation* safetyViolation; |
5eb9927a | 350 | |
7e7ebe3e IB |
351 | /// See the `FUNCFLAG` struct |
352 | import dmd.common.bitfields; | |
353 | mixin(generateBitFields!(FUNCFLAG, uint)); | |
5fee5ec3 IB |
354 | |
355 | /** | |
356 | * Data for a function declaration that is needed for the Objective-C | |
357 | * integration. | |
358 | */ | |
359 | ObjcFuncDeclaration objc; | |
360 | ||
361 | extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) | |
362 | { | |
363 | super(loc, ident); | |
364 | //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type); | |
365 | //printf("storage_class = x%x\n", storage_class); | |
366 | this.storage_class = storage_class; | |
367 | this.type = type; | |
368 | if (type) | |
369 | { | |
370 | // Normalize storage_class, because function-type related attributes | |
371 | // are already set in the 'type' in parsing phase. | |
372 | this.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR); | |
373 | } | |
374 | this.endloc = endloc; | |
375 | if (noreturn) | |
7e7ebe3e | 376 | this.noreturn = true; |
5fee5ec3 IB |
377 | |
378 | /* The type given for "infer the return type" is a TypeFunction with | |
379 | * NULL for the return type. | |
380 | */ | |
235d5a96 | 381 | if (type && type.nextOf() is null) |
7e7ebe3e | 382 | this.inferRetType = true; |
5fee5ec3 IB |
383 | } |
384 | ||
385 | static FuncDeclaration create(const ref Loc loc, const ref Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) | |
386 | { | |
387 | return new FuncDeclaration(loc, endloc, id, storage_class, type, noreturn); | |
388 | } | |
389 | ||
390 | override FuncDeclaration syntaxCopy(Dsymbol s) | |
391 | { | |
392 | //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); | |
393 | FuncDeclaration f = s ? cast(FuncDeclaration)s | |
7e7ebe3e | 394 | : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy(), this.noreturn != 0); |
5fee5ec3 IB |
395 | f.frequires = frequires ? Statement.arraySyntaxCopy(frequires) : null; |
396 | f.fensures = fensures ? Ensure.arraySyntaxCopy(fensures) : null; | |
397 | f.fbody = fbody ? fbody.syntaxCopy() : null; | |
398 | return f; | |
399 | } | |
400 | ||
401 | /**************************************************** | |
402 | * Resolve forward reference of function signature - | |
403 | * parameter types, return type, and attributes. | |
404 | * Returns: | |
405 | * false if any errors exist in the signature. | |
406 | */ | |
407 | final bool functionSemantic() | |
408 | { | |
409 | //printf("functionSemantic() %p %s\n", this, toChars()); | |
410 | if (!_scope) | |
411 | return !errors; | |
412 | ||
413 | this.cppnamespace = _scope.namespace; | |
414 | ||
415 | if (!originalType) // semantic not yet run | |
416 | { | |
417 | TemplateInstance spec = isSpeculative(); | |
418 | uint olderrs = global.errors; | |
419 | uint oldgag = global.gag; | |
420 | if (global.gag && !spec) | |
421 | global.gag = 0; | |
422 | dsymbolSemantic(this, _scope); | |
423 | global.gag = oldgag; | |
424 | if (spec && global.errors != olderrs) | |
425 | spec.errors = (global.errors - olderrs != 0); | |
426 | if (olderrs != global.errors) // if errors compiling this function | |
427 | return false; | |
428 | } | |
429 | ||
430 | // if inferring return type, sematic3 needs to be run | |
431 | // - When the function body contains any errors, we cannot assume | |
432 | // the inferred return type is valid. | |
433 | // So, the body errors should become the function signature error. | |
434 | if (inferRetType && type && !type.nextOf()) | |
435 | return functionSemantic3(); | |
436 | ||
437 | TemplateInstance ti; | |
438 | if (isInstantiated() && !isVirtualMethod() && | |
439 | ((ti = parent.isTemplateInstance()) is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident)) | |
440 | { | |
441 | AggregateDeclaration ad = isMemberLocal(); | |
442 | if (ad && ad.sizeok != Sizeok.done) | |
443 | { | |
444 | /* Currently dmd cannot resolve forward references per methods, | |
445 | * then setting SIZOKfwd is too conservative and would break existing code. | |
446 | * So, just stop method attributes inference until ad.dsymbolSemantic() done. | |
447 | */ | |
448 | //ad.sizeok = Sizeok.fwd; | |
449 | } | |
450 | else | |
451 | return functionSemantic3() || !errors; | |
452 | } | |
453 | ||
454 | if (storage_class & STC.inference) | |
455 | return functionSemantic3() || !errors; | |
456 | ||
457 | return !errors; | |
458 | } | |
459 | ||
460 | /**************************************************** | |
461 | * Resolve forward reference of function body. | |
462 | * Returns false if any errors exist in the body. | |
463 | */ | |
464 | final bool functionSemantic3() | |
465 | { | |
466 | if (semanticRun < PASS.semantic3 && _scope) | |
467 | { | |
468 | /* Forward reference - we need to run semantic3 on this function. | |
469 | * If errors are gagged, and it's not part of a template instance, | |
470 | * we need to temporarily ungag errors. | |
471 | */ | |
472 | TemplateInstance spec = isSpeculative(); | |
473 | uint olderrs = global.errors; | |
474 | uint oldgag = global.gag; | |
475 | if (global.gag && !spec) | |
476 | global.gag = 0; | |
477 | semantic3(this, _scope); | |
478 | global.gag = oldgag; | |
479 | ||
480 | // If it is a speculatively-instantiated template, and errors occur, | |
481 | // we need to mark the template as having errors. | |
482 | if (spec && global.errors != olderrs) | |
483 | spec.errors = (global.errors - olderrs != 0); | |
484 | if (olderrs != global.errors) // if errors compiling this function | |
485 | return false; | |
486 | } | |
487 | ||
235d5a96 | 488 | return !errors && !this.hasSemantic3Errors(); |
5fee5ec3 IB |
489 | } |
490 | ||
491 | /**************************************************** | |
492 | * Check that this function type is properly resolved. | |
493 | * If not, report "forward reference error" and return true. | |
494 | */ | |
495 | extern (D) final bool checkForwardRef(const ref Loc loc) | |
496 | { | |
497 | if (!functionSemantic()) | |
498 | return true; | |
499 | ||
500 | /* No deco means the functionSemantic() call could not resolve | |
501 | * forward referenes in the type of this function. | |
502 | */ | |
503 | if (!type.deco) | |
504 | { | |
505 | bool inSemantic3 = (inferRetType && semanticRun >= PASS.semantic3); | |
506 | .error(loc, "forward reference to %s`%s`", | |
507 | (inSemantic3 ? "inferred return type of function " : "").ptr, | |
508 | toChars()); | |
509 | return true; | |
510 | } | |
511 | return false; | |
512 | } | |
513 | ||
514 | // called from semantic3 | |
515 | /** | |
516 | * Creates and returns the hidden parameters for this function declaration. | |
517 | * | |
518 | * Hidden parameters include the `this` parameter of a class, struct or | |
519 | * nested function and the selector parameter for Objective-C methods. | |
520 | */ | |
521 | extern (D) final void declareThis(Scope* sc) | |
522 | { | |
235d5a96 IB |
523 | const bool dualCtx = (toParent2() != toParentLocal()); |
524 | if (dualCtx) | |
7e7ebe3e | 525 | this.hasDualContext = true; |
5fee5ec3 | 526 | auto ad = isThis(); |
235d5a96 | 527 | if (!dualCtx && !ad && !isNested()) |
5fee5ec3 IB |
528 | { |
529 | vthis = null; | |
530 | objc.selectorParameter = null; | |
531 | return; | |
532 | } | |
533 | ||
534 | Type addModStc(Type t) | |
535 | { | |
536 | return t.addMod(type.mod).addStorageClass(storage_class); | |
537 | } | |
538 | ||
235d5a96 | 539 | if (dualCtx || isNested()) |
5fee5ec3 IB |
540 | { |
541 | /* The 'this' for a nested function is the link to the | |
542 | * enclosing function's stack frame. | |
543 | * Note that nested functions and member functions are disjoint. | |
544 | */ | |
235d5a96 | 545 | Type tthis = addModStc(dualCtx ? |
5fee5ec3 IB |
546 | Type.tvoidptr.sarrayOf(2).pointerTo() : |
547 | Type.tvoid.pointerTo()); | |
235d5a96 | 548 | vthis = new VarDeclaration(loc, tthis, dualCtx ? Id.this2 : Id.capture, null); |
5fee5ec3 IB |
549 | vthis.storage_class |= STC.parameter | STC.nodtor; |
550 | } | |
551 | else if (ad) | |
552 | { | |
553 | Type thandle = addModStc(ad.handleType()); | |
554 | vthis = new ThisDeclaration(loc, thandle); | |
555 | vthis.storage_class |= STC.parameter; | |
556 | if (thandle.ty == Tstruct) | |
557 | { | |
558 | vthis.storage_class |= STC.ref_; | |
5fee5ec3 IB |
559 | } |
560 | } | |
561 | ||
562 | if (auto tf = type.isTypeFunction()) | |
563 | { | |
564 | if (tf.isreturn) | |
565 | vthis.storage_class |= STC.return_; | |
566 | if (tf.isScopeQual) | |
567 | vthis.storage_class |= STC.scope_; | |
235d5a96 | 568 | if (tf.isreturnscope) |
5fee5ec3 | 569 | vthis.storage_class |= STC.returnScope; |
5fee5ec3 | 570 | } |
5fee5ec3 IB |
571 | |
572 | vthis.dsymbolSemantic(sc); | |
573 | if (!sc.insert(vthis)) | |
574 | assert(0); | |
575 | vthis.parent = this; | |
576 | if (ad) | |
577 | objc.selectorParameter = .objc.createSelectorParameter(this, sc); | |
578 | } | |
579 | ||
580 | override final bool equals(const RootObject o) const | |
581 | { | |
582 | if (this == o) | |
583 | return true; | |
584 | ||
585 | if (auto s = isDsymbol(o)) | |
586 | { | |
587 | auto fd1 = this; | |
588 | auto fd2 = s.isFuncDeclaration(); | |
589 | if (!fd2) | |
590 | return false; | |
591 | ||
592 | auto fa1 = fd1.isFuncAliasDeclaration(); | |
593 | auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; | |
594 | ||
595 | auto fa2 = fd2.isFuncAliasDeclaration(); | |
596 | auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; | |
597 | ||
598 | if (fa1 && fa2) | |
599 | { | |
600 | return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; | |
601 | } | |
602 | ||
603 | bool b1 = fa1 !is null; | |
604 | if (b1 && faf1.isUnique() && !fa1.hasOverloads) | |
605 | b1 = false; | |
606 | ||
607 | bool b2 = fa2 !is null; | |
608 | if (b2 && faf2.isUnique() && !fa2.hasOverloads) | |
609 | b2 = false; | |
610 | ||
611 | if (b1 != b2) | |
612 | return false; | |
613 | ||
614 | return faf1.toParent().equals(faf2.toParent()) && | |
615 | faf1.ident.equals(faf2.ident) && | |
616 | faf1.type.equals(faf2.type); | |
617 | } | |
618 | return false; | |
619 | } | |
620 | ||
621 | /**************************************************** | |
622 | * Determine if 'this' overrides fd. | |
623 | * Return !=0 if it does. | |
624 | */ | |
625 | final int overrides(FuncDeclaration fd) | |
626 | { | |
627 | int result = 0; | |
628 | if (fd.ident == ident) | |
629 | { | |
630 | const cov = type.covariant(fd.type); | |
631 | if (cov != Covariant.distinct) | |
632 | { | |
633 | ClassDeclaration cd1 = toParent().isClassDeclaration(); | |
634 | ClassDeclaration cd2 = fd.toParent().isClassDeclaration(); | |
635 | if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) | |
636 | result = 1; | |
637 | } | |
638 | } | |
639 | return result; | |
640 | } | |
641 | ||
642 | /************************************************* | |
643 | * Find index of function in vtbl[0..dim] that | |
644 | * this function overrides. | |
645 | * Prefer an exact match to a covariant one. | |
646 | * Params: | |
647 | * vtbl = vtable to use | |
648 | * dim = maximal vtable dimension | |
649 | * Returns: | |
650 | * -1 didn't find one | |
651 | * -2 can't determine because of forward references | |
652 | */ | |
653 | final int findVtblIndex(Dsymbols* vtbl, int dim) | |
654 | { | |
655 | //printf("findVtblIndex() %s\n", toChars()); | |
656 | FuncDeclaration mismatch = null; | |
657 | StorageClass mismatchstc = 0; | |
658 | int mismatchvi = -1; | |
659 | int exactvi = -1; | |
660 | int bestvi = -1; | |
661 | for (int vi = 0; vi < dim; vi++) | |
662 | { | |
663 | FuncDeclaration fdv = (*vtbl)[vi].isFuncDeclaration(); | |
664 | if (fdv && fdv.ident == ident) | |
665 | { | |
666 | if (type.equals(fdv.type)) // if exact match | |
667 | { | |
668 | if (fdv.parent.isClassDeclaration()) | |
669 | { | |
670 | if (fdv.isFuture()) | |
671 | { | |
672 | bestvi = vi; | |
673 | continue; // keep looking | |
674 | } | |
675 | return vi; // no need to look further | |
676 | } | |
677 | ||
678 | if (exactvi >= 0) | |
679 | { | |
680 | error("cannot determine overridden function"); | |
681 | return exactvi; | |
682 | } | |
683 | exactvi = vi; | |
684 | bestvi = vi; | |
685 | continue; | |
686 | } | |
687 | ||
688 | StorageClass stc = 0; | |
689 | const cov = type.covariant(fdv.type, &stc); | |
690 | //printf("\tbaseclass cov = %d\n", cov); | |
691 | final switch (cov) | |
692 | { | |
693 | case Covariant.distinct: | |
694 | // types are distinct | |
695 | break; | |
696 | ||
697 | case Covariant.yes: | |
698 | bestvi = vi; // covariant, but not identical | |
699 | break; | |
700 | // keep looking for an exact match | |
701 | ||
702 | case Covariant.no: | |
703 | mismatchvi = vi; | |
704 | mismatchstc = stc; | |
705 | mismatch = fdv; // overrides, but is not covariant | |
706 | break; | |
707 | // keep looking for an exact match | |
708 | ||
709 | case Covariant.fwdref: | |
710 | return -2; // forward references | |
711 | } | |
712 | } | |
713 | } | |
610d7898 IB |
714 | if (_linkage == LINK.cpp && bestvi != -1) |
715 | { | |
716 | StorageClass stc = 0; | |
717 | FuncDeclaration fdv = (*vtbl)[bestvi].isFuncDeclaration(); | |
718 | assert(fdv && fdv.ident == ident); | |
719 | if (type.covariant(fdv.type, &stc, /*cppCovariant=*/true) == Covariant.no) | |
720 | { | |
721 | /* https://issues.dlang.org/show_bug.cgi?id=22351 | |
722 | * Under D rules, `type` and `fdv.type` are covariant, but under C++ rules, they are not. | |
723 | * For now, continue to allow D covariant rules to apply when `override` has been used, | |
724 | * but issue a deprecation warning that this behaviour will change in the future. | |
725 | * Otherwise, follow the C++ covariant rules, which will create a new vtable entry. | |
726 | */ | |
727 | if (isOverride()) | |
728 | { | |
729 | /* @@@DEPRECATED_2.110@@@ | |
730 | * After deprecation period has ended, be sure to remove this entire `LINK.cpp` branch, | |
731 | * but also the `cppCovariant` parameter from Type.covariant, and update the function | |
732 | * so that both `LINK.cpp` covariant conditions within are always checked. | |
733 | */ | |
734 | .deprecation(loc, "overriding `extern(C++)` function `%s%s` with `const` qualified function `%s%s%s` is deprecated", | |
735 | fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(), | |
736 | toPrettyChars(), type.toTypeFunction().parameterList.parametersTypeToChars(), type.modToChars()); | |
737 | ||
738 | const char* where = type.isNaked() ? "parameters" : "type"; | |
739 | deprecationSupplemental(loc, "Either remove `override`, or adjust the `const` qualifiers of the " | |
740 | ~ "overriding function %s", where); | |
741 | } | |
742 | else | |
743 | { | |
744 | // Treat as if Covariant.no | |
745 | mismatchvi = bestvi; | |
746 | mismatchstc = stc; | |
747 | mismatch = fdv; | |
748 | bestvi = -1; | |
749 | } | |
750 | } | |
751 | } | |
5fee5ec3 IB |
752 | if (bestvi == -1 && mismatch) |
753 | { | |
754 | //type.print(); | |
755 | //mismatch.type.print(); | |
756 | //printf("%s %s\n", type.deco, mismatch.type.deco); | |
757 | //printf("stc = %llx\n", mismatchstc); | |
758 | if (mismatchstc) | |
759 | { | |
760 | // Fix it by modifying the type to add the storage classes | |
761 | type = type.addStorageClass(mismatchstc); | |
762 | bestvi = mismatchvi; | |
763 | } | |
764 | } | |
765 | return bestvi; | |
766 | } | |
767 | ||
768 | /********************************* | |
769 | * If function a function in a base class, | |
770 | * return that base class. | |
771 | * Returns: | |
772 | * base class if overriding, null if not | |
773 | */ | |
774 | final BaseClass* overrideInterface() | |
775 | { | |
0fb57034 | 776 | for (ClassDeclaration cd = toParent2().isClassDeclaration(); cd; cd = cd.baseClass) |
5fee5ec3 IB |
777 | { |
778 | foreach (b; cd.interfaces) | |
779 | { | |
780 | auto v = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim); | |
781 | if (v >= 0) | |
782 | return b; | |
783 | } | |
784 | } | |
785 | return null; | |
786 | } | |
787 | ||
788 | /**************************************************** | |
789 | * Overload this FuncDeclaration with the new one f. | |
790 | * Return true if successful; i.e. no conflict. | |
791 | */ | |
792 | override bool overloadInsert(Dsymbol s) | |
793 | { | |
794 | //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars()); | |
795 | assert(s != this); | |
796 | AliasDeclaration ad = s.isAliasDeclaration(); | |
797 | if (ad) | |
798 | { | |
799 | if (overnext) | |
800 | return overnext.overloadInsert(ad); | |
801 | if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof) | |
802 | { | |
803 | //printf("\tad = '%s'\n", ad.type.toChars()); | |
804 | return false; | |
805 | } | |
806 | overnext = ad; | |
807 | //printf("\ttrue: no conflict\n"); | |
808 | return true; | |
809 | } | |
810 | TemplateDeclaration td = s.isTemplateDeclaration(); | |
811 | if (td) | |
812 | { | |
813 | if (!td.funcroot) | |
814 | td.funcroot = this; | |
815 | if (overnext) | |
816 | return overnext.overloadInsert(td); | |
817 | overnext = td; | |
818 | return true; | |
819 | } | |
820 | FuncDeclaration fd = s.isFuncDeclaration(); | |
821 | if (!fd) | |
822 | return false; | |
823 | ||
824 | version (none) | |
825 | { | |
826 | /* Disable this check because: | |
827 | * const void foo(); | |
828 | * semantic() isn't run yet on foo(), so the const hasn't been | |
829 | * applied yet. | |
830 | */ | |
831 | if (type) | |
832 | { | |
833 | printf("type = %s\n", type.toChars()); | |
834 | printf("fd.type = %s\n", fd.type.toChars()); | |
835 | } | |
836 | // fd.type can be NULL for overloaded constructors | |
837 | if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration()) | |
838 | { | |
839 | //printf("\tfalse: conflict %s\n", kind()); | |
840 | return false; | |
841 | } | |
842 | } | |
843 | ||
844 | if (overnext) | |
845 | { | |
846 | td = overnext.isTemplateDeclaration(); | |
847 | if (td) | |
848 | fd.overloadInsert(td); | |
849 | else | |
850 | return overnext.overloadInsert(fd); | |
851 | } | |
852 | overnext = fd; | |
853 | //printf("\ttrue: no conflict\n"); | |
854 | return true; | |
855 | } | |
856 | ||
857 | /******************************************** | |
858 | * Find function in overload list that exactly matches t. | |
859 | */ | |
860 | extern (D) final FuncDeclaration overloadExactMatch(Type t) | |
861 | { | |
862 | FuncDeclaration fd; | |
863 | overloadApply(this, (Dsymbol s) | |
864 | { | |
865 | auto f = s.isFuncDeclaration(); | |
866 | if (!f) | |
867 | return 0; | |
b6df1132 IB |
868 | if (f.storage_class & STC.disable) |
869 | return 0; | |
5fee5ec3 IB |
870 | if (t.equals(f.type)) |
871 | { | |
872 | fd = f; | |
873 | return 1; | |
874 | } | |
875 | ||
876 | /* Allow covariant matches, as long as the return type | |
877 | * is just a const conversion. | |
878 | * This allows things like pure functions to match with an impure function type. | |
879 | */ | |
880 | if (t.ty == Tfunction) | |
881 | { | |
882 | auto tf = cast(TypeFunction)f.type; | |
883 | if (tf.covariant(t) == Covariant.yes && | |
884 | tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) | |
885 | { | |
886 | fd = f; | |
887 | return 1; | |
888 | } | |
889 | } | |
890 | return 0; | |
891 | }); | |
892 | return fd; | |
893 | } | |
894 | ||
895 | /******************************************** | |
896 | * Find function in overload list that matches to the 'this' modifier. | |
897 | * There's four result types. | |
898 | * | |
899 | * 1. If the 'tthis' matches only one candidate, it's an "exact match". | |
900 | * Returns the function and 'hasOverloads' is set to false. | |
901 | * eg. If 'tthis" is mutable and there's only one mutable method. | |
902 | * 2. If there's two or more match candidates, but a candidate function will be | |
903 | * a "better match". | |
904 | * Returns the better match function but 'hasOverloads' is set to true. | |
905 | * eg. If 'tthis' is mutable, and there's both mutable and const methods, | |
906 | * the mutable method will be a better match. | |
907 | * 3. If there's two or more match candidates, but there's no better match, | |
908 | * Returns null and 'hasOverloads' is set to true to represent "ambiguous match". | |
909 | * eg. If 'tthis' is mutable, and there's two or more mutable methods. | |
910 | * 4. If there's no candidates, it's "no match" and returns null with error report. | |
911 | * e.g. If 'tthis' is const but there's no const methods. | |
912 | */ | |
913 | extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads) | |
914 | { | |
915 | //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); | |
916 | MatchAccumulator m; | |
917 | overloadApply(this, (Dsymbol s) | |
918 | { | |
919 | auto f = s.isFuncDeclaration(); | |
920 | if (!f || f == m.lastf) // skip duplicates | |
921 | return 0; | |
922 | ||
923 | auto tf = f.type.toTypeFunction(); | |
924 | //printf("tf = %s\n", tf.toChars()); | |
925 | ||
926 | MATCH match; | |
927 | if (tthis) // non-static functions are preferred than static ones | |
928 | { | |
929 | if (f.needThis()) | |
930 | match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); | |
931 | else | |
932 | match = MATCH.constant; // keep static function in overload candidates | |
933 | } | |
934 | else // static functions are preferred than non-static ones | |
935 | { | |
936 | if (f.needThis()) | |
937 | match = MATCH.convert; | |
938 | else | |
939 | match = MATCH.exact; | |
940 | } | |
941 | if (match == MATCH.nomatch) | |
942 | return 0; | |
943 | ||
944 | if (match > m.last) goto LcurrIsBetter; | |
945 | if (match < m.last) goto LlastIsBetter; | |
946 | ||
947 | // See if one of the matches overrides the other. | |
948 | if (m.lastf.overrides(f)) goto LlastIsBetter; | |
949 | if (f.overrides(m.lastf)) goto LcurrIsBetter; | |
950 | ||
951 | //printf("\tambiguous\n"); | |
952 | m.nextf = f; | |
953 | m.count++; | |
954 | return 0; | |
955 | ||
956 | LlastIsBetter: | |
957 | //printf("\tlastbetter\n"); | |
958 | m.count++; // count up | |
959 | return 0; | |
960 | ||
961 | LcurrIsBetter: | |
962 | //printf("\tisbetter\n"); | |
963 | if (m.last <= MATCH.convert) | |
964 | { | |
965 | // clear last secondary matching | |
966 | m.nextf = null; | |
967 | m.count = 0; | |
968 | } | |
969 | m.last = match; | |
970 | m.lastf = f; | |
971 | m.count++; // count up | |
972 | return 0; | |
973 | }); | |
974 | ||
975 | if (m.count == 1) // exact match | |
976 | { | |
977 | hasOverloads = false; | |
978 | } | |
979 | else if (m.count > 1) // better or ambiguous match | |
980 | { | |
981 | hasOverloads = true; | |
982 | } | |
983 | else // no match | |
984 | { | |
985 | hasOverloads = true; | |
986 | auto tf = this.type.toTypeFunction(); | |
987 | assert(tthis); | |
988 | assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch | |
989 | { | |
990 | OutBuffer thisBuf, funcBuf; | |
991 | MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); | |
992 | MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); | |
993 | .error(loc, "%smethod %s is not callable using a %sobject", | |
994 | funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars()); | |
995 | } | |
996 | } | |
997 | return m.lastf; | |
998 | } | |
999 | ||
1000 | /******************************************** | |
1001 | * find function template root in overload list | |
1002 | */ | |
1003 | extern (D) final TemplateDeclaration findTemplateDeclRoot() | |
1004 | { | |
1005 | FuncDeclaration f = this; | |
1006 | while (f && f.overnext) | |
1007 | { | |
1008 | //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars()); | |
1009 | TemplateDeclaration td = f.overnext.isTemplateDeclaration(); | |
1010 | if (td) | |
1011 | return td; | |
1012 | f = f.overnext.isFuncDeclaration(); | |
1013 | } | |
1014 | return null; | |
1015 | } | |
1016 | ||
1017 | /******************************************** | |
1018 | * Returns true if function was declared | |
1019 | * directly or indirectly in a unittest block | |
1020 | */ | |
1021 | final bool inUnittest() | |
1022 | { | |
1023 | Dsymbol f = this; | |
1024 | do | |
1025 | { | |
1026 | if (f.isUnitTestDeclaration()) | |
1027 | return true; | |
1028 | f = f.toParent(); | |
1029 | } | |
1030 | while (f); | |
1031 | return false; | |
1032 | } | |
1033 | ||
1034 | /************************************* | |
1035 | * Determine partial specialization order of 'this' vs g. | |
1036 | * This is very similar to TemplateDeclaration::leastAsSpecialized(). | |
1037 | * Returns: | |
1038 | * match 'this' is at least as specialized as g | |
1039 | * 0 g is more specialized than 'this' | |
1040 | */ | |
1041 | final MATCH leastAsSpecialized(FuncDeclaration g) | |
1042 | { | |
1043 | enum LOG_LEASTAS = 0; | |
1044 | static if (LOG_LEASTAS) | |
1045 | { | |
1046 | printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars()); | |
1047 | printf("%s, %s\n", type.toChars(), g.type.toChars()); | |
1048 | } | |
1049 | ||
1050 | /* This works by calling g() with f()'s parameters, and | |
1051 | * if that is possible, then f() is at least as specialized | |
1052 | * as g() is. | |
1053 | */ | |
1054 | ||
1055 | TypeFunction tf = type.toTypeFunction(); | |
1056 | TypeFunction tg = g.type.toTypeFunction(); | |
1057 | ||
1058 | /* If both functions have a 'this' pointer, and the mods are not | |
1059 | * the same and g's is not const, then this is less specialized. | |
1060 | */ | |
1061 | if (needThis() && g.needThis() && tf.mod != tg.mod) | |
1062 | { | |
1063 | if (isCtorDeclaration()) | |
1064 | { | |
1065 | if (!MODimplicitConv(tg.mod, tf.mod)) | |
1066 | return MATCH.nomatch; | |
1067 | } | |
1068 | else | |
1069 | { | |
1070 | if (!MODimplicitConv(tf.mod, tg.mod)) | |
1071 | return MATCH.nomatch; | |
1072 | } | |
1073 | } | |
1074 | ||
1075 | /* Create a dummy array of arguments out of the parameters to f() | |
1076 | */ | |
1077 | Expressions args; | |
1078 | foreach (u, p; tf.parameterList) | |
1079 | { | |
1080 | Expression e; | |
1081 | if (p.isReference()) | |
1082 | { | |
1083 | e = new IdentifierExp(Loc.initial, p.ident); | |
1084 | e.type = p.type; | |
1085 | } | |
1086 | else | |
1087 | e = p.type.defaultInitLiteral(Loc.initial); | |
1088 | args.push(e); | |
1089 | } | |
1090 | ||
1091 | MATCH m = tg.callMatch(null, args[], 1); | |
1092 | if (m > MATCH.nomatch) | |
1093 | { | |
1094 | /* A variadic parameter list is less specialized than a | |
1095 | * non-variadic one. | |
1096 | */ | |
1097 | if (tf.parameterList.varargs && !tg.parameterList.varargs) | |
1098 | goto L1; // less specialized | |
1099 | ||
1100 | static if (LOG_LEASTAS) | |
1101 | { | |
1102 | printf(" matches %d, so is least as specialized\n", m); | |
1103 | } | |
1104 | return m; | |
1105 | } | |
1106 | L1: | |
1107 | static if (LOG_LEASTAS) | |
1108 | { | |
1109 | printf(" doesn't match, so is not as specialized\n"); | |
1110 | } | |
1111 | return MATCH.nomatch; | |
1112 | } | |
1113 | ||
1114 | /******************************** | |
1115 | * Searches for a label with the given identifier. This function will insert a new | |
1116 | * `LabelDsymbol` into `labtab` if it does not contain a mapping for `ident`. | |
1117 | * | |
1118 | * Params: | |
1119 | * ident = identifier of the requested label | |
1120 | * loc = location used when creating a new `LabelDsymbol` | |
1121 | * | |
1122 | * Returns: the `LabelDsymbol` for `ident` | |
1123 | */ | |
1124 | final LabelDsymbol searchLabel(Identifier ident, const ref Loc loc = Loc.initial) | |
1125 | { | |
1126 | Dsymbol s; | |
1127 | if (!labtab) | |
1128 | labtab = new DsymbolTable(); // guess we need one | |
1129 | ||
1130 | s = labtab.lookup(ident); | |
1131 | if (!s) | |
1132 | { | |
1133 | s = new LabelDsymbol(ident, loc); | |
1134 | labtab.insert(s); | |
1135 | } | |
1136 | return cast(LabelDsymbol)s; | |
1137 | } | |
1138 | ||
1139 | /***************************************** | |
1140 | * Determine lexical level difference from `this` to nested function `fd`. | |
1141 | * Params: | |
1142 | * fd = target of call | |
1143 | * intypeof = !=0 if inside typeof | |
1144 | * Returns: | |
1145 | * 0 same level | |
1146 | * >0 decrease nesting by number | |
1147 | * -1 increase nesting by 1 (`fd` is nested within `this`) | |
1148 | * LevelError error, `this` cannot call `fd` | |
1149 | */ | |
1150 | final int getLevel(FuncDeclaration fd, int intypeof) | |
1151 | { | |
1152 | //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars()); | |
1153 | Dsymbol fdparent = fd.toParent2(); | |
1154 | if (fdparent == this) | |
1155 | return -1; | |
1156 | ||
1157 | Dsymbol s = this; | |
1158 | int level = 0; | |
1159 | while (fd != s && fdparent != s.toParent2()) | |
1160 | { | |
1161 | //printf("\ts = %s, '%s'\n", s.kind(), s.toChars()); | |
1162 | if (auto thisfd = s.isFuncDeclaration()) | |
1163 | { | |
1164 | if (!thisfd.isNested() && !thisfd.vthis && !intypeof) | |
1165 | return LevelError; | |
1166 | } | |
1167 | else | |
1168 | { | |
1169 | if (auto thiscd = s.isAggregateDeclaration()) | |
1170 | { | |
1171 | /* AggregateDeclaration::isNested returns true only when | |
1172 | * it has a hidden pointer. | |
1173 | * But, calling the function belongs unrelated lexical scope | |
1174 | * is still allowed inside typeof. | |
1175 | * | |
1176 | * struct Map(alias fun) { | |
1177 | * typeof({ return fun(); }) RetType; | |
1178 | * // No member function makes Map struct 'not nested'. | |
1179 | * } | |
1180 | */ | |
1181 | if (!thiscd.isNested() && !intypeof) | |
1182 | return LevelError; | |
1183 | } | |
1184 | else | |
1185 | return LevelError; | |
1186 | } | |
1187 | ||
1188 | s = s.toParentP(fd); | |
1189 | assert(s); | |
1190 | level++; | |
1191 | } | |
1192 | return level; | |
1193 | } | |
1194 | ||
1195 | /*********************************** | |
1196 | * Determine lexical level difference from `this` to nested function `fd`. | |
1197 | * Issue error if `this` cannot call `fd`. | |
1198 | * | |
1199 | * Params: | |
1200 | * loc = location for error messages | |
1201 | * sc = context | |
1202 | * fd = target of call | |
1203 | * decl = The `Declaration` that triggered this check. | |
1204 | * Used to provide a better error message only. | |
1205 | * Returns: | |
1206 | * 0 same level | |
1207 | * >0 decrease nesting by number | |
1208 | * -1 increase nesting by 1 (`fd` is nested within 'this') | |
1209 | * LevelError error | |
1210 | */ | |
1211 | final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd, | |
1212 | Declaration decl) | |
1213 | { | |
1214 | int level = getLevel(fd, sc.intypeof); | |
1215 | if (level != LevelError) | |
1216 | return level; | |
1217 | ||
1218 | // Don't give error if in template constraint | |
1219 | if (!(sc.flags & SCOPE.constraint)) | |
1220 | { | |
1221 | const(char)* xstatic = isStatic() ? "`static` " : ""; | |
1222 | // better diagnostics for static functions | |
1223 | .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`", | |
1224 | xstatic, kind(), toPrettyChars(), decl.kind(), decl.toChars(), | |
1225 | fd.toPrettyChars()); | |
1226 | .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars()); | |
1227 | return LevelError; | |
1228 | } | |
1229 | return 1; | |
1230 | } | |
1231 | ||
1232 | enum LevelError = -2; | |
1233 | ||
1234 | override const(char)* toPrettyChars(bool QualifyTypes = false) | |
1235 | { | |
1236 | if (isMain()) | |
1237 | return "D main"; | |
1238 | else | |
1239 | return Dsymbol.toPrettyChars(QualifyTypes); | |
1240 | } | |
1241 | ||
1242 | /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ | |
1243 | final const(char)* toFullSignature() | |
1244 | { | |
1245 | OutBuffer buf; | |
1246 | functionToBufferWithIdent(type.toTypeFunction(), &buf, toChars(), isStatic); | |
1247 | return buf.extractChars(); | |
1248 | } | |
1249 | ||
1250 | final bool isMain() const | |
1251 | { | |
5eb9927a | 1252 | return ident == Id.main && resolvedLinkage() != LINK.c && !isMember() && !isNested(); |
5fee5ec3 IB |
1253 | } |
1254 | ||
1255 | final bool isCMain() const | |
1256 | { | |
5eb9927a | 1257 | return ident == Id.main && resolvedLinkage() == LINK.c && !isMember() && !isNested(); |
5fee5ec3 IB |
1258 | } |
1259 | ||
1260 | final bool isWinMain() const | |
1261 | { | |
1262 | //printf("FuncDeclaration::isWinMain() %s\n", toChars()); | |
1263 | version (none) | |
1264 | { | |
5eb9927a | 1265 | bool x = ident == Id.WinMain && resolvedLinkage() != LINK.c && !isMember(); |
5fee5ec3 IB |
1266 | printf("%s\n", x ? "yes" : "no"); |
1267 | return x; | |
1268 | } | |
1269 | else | |
1270 | { | |
5eb9927a | 1271 | return ident == Id.WinMain && resolvedLinkage() != LINK.c && !isMember(); |
5fee5ec3 IB |
1272 | } |
1273 | } | |
1274 | ||
1275 | final bool isDllMain() const | |
1276 | { | |
5eb9927a | 1277 | return ident == Id.DllMain && resolvedLinkage() != LINK.c && !isMember(); |
5fee5ec3 IB |
1278 | } |
1279 | ||
1280 | final bool isRtInit() const | |
1281 | { | |
5eb9927a | 1282 | return ident == Id.rt_init && resolvedLinkage() == LINK.c && !isMember() && !isNested(); |
5fee5ec3 IB |
1283 | } |
1284 | ||
1285 | override final bool isExport() const | |
1286 | { | |
1287 | return visibility.kind == Visibility.Kind.export_; | |
1288 | } | |
1289 | ||
1290 | override final bool isImportedSymbol() const | |
1291 | { | |
1292 | //printf("isImportedSymbol()\n"); | |
1293 | //printf("protection = %d\n", visibility); | |
1294 | return (visibility.kind == Visibility.Kind.export_) && !fbody; | |
1295 | } | |
1296 | ||
1297 | override final bool isCodeseg() const pure nothrow @nogc @safe | |
1298 | { | |
1299 | return true; // functions are always in the code segment | |
1300 | } | |
1301 | ||
1302 | override final bool isOverloadable() const | |
1303 | { | |
1304 | return true; // functions can be overloaded | |
1305 | } | |
1306 | ||
1307 | /*********************************** | |
1308 | * Override so it can work even if semantic() hasn't yet | |
1309 | * been run. | |
1310 | */ | |
1311 | override final bool isAbstract() | |
1312 | { | |
1313 | if (storage_class & STC.abstract_) | |
1314 | return true; | |
1315 | if (semanticRun >= PASS.semanticdone) | |
1316 | return false; | |
1317 | ||
1318 | if (_scope) | |
1319 | { | |
1320 | if (_scope.stc & STC.abstract_) | |
1321 | return true; | |
1322 | parent = _scope.parent; | |
1323 | Dsymbol parent = toParent(); | |
1324 | if (parent.isInterfaceDeclaration()) | |
1325 | return true; | |
1326 | } | |
1327 | return false; | |
1328 | } | |
1329 | ||
1330 | /********************************** | |
1331 | * Decide if attributes for this function can be inferred from examining | |
1332 | * the function body. | |
1333 | * Returns: | |
1334 | * true if can | |
1335 | */ | |
1336 | final bool canInferAttributes(Scope* sc) | |
1337 | { | |
1338 | if (!fbody) | |
1339 | return false; | |
1340 | ||
9c7d5e88 IB |
1341 | if (isVirtualMethod() && |
1342 | /* | |
1343 | * https://issues.dlang.org/show_bug.cgi?id=21719 | |
1344 | * | |
1345 | * If we have an auto virtual function we can infer | |
1346 | * the attributes. | |
1347 | */ | |
1348 | !(inferRetType && !isCtorDeclaration())) | |
5fee5ec3 IB |
1349 | return false; // since they may be overridden |
1350 | ||
1351 | if (sc.func && | |
1352 | /********** this is for backwards compatibility for the moment ********/ | |
1353 | (!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated())) | |
1354 | return true; | |
1355 | ||
1356 | if (isFuncLiteralDeclaration() || // externs are not possible with literals | |
1357 | (storage_class & STC.inference) || // do attribute inference | |
1358 | (inferRetType && !isCtorDeclaration())) | |
1359 | return true; | |
1360 | ||
1361 | if (isInstantiated()) | |
1362 | { | |
1363 | auto ti = parent.isTemplateInstance(); | |
1364 | if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident) | |
1365 | return true; | |
1366 | } | |
1367 | ||
1368 | return false; | |
1369 | } | |
1370 | ||
1371 | /***************************************** | |
1372 | * Initialize for inferring the attributes of this function. | |
1373 | */ | |
1374 | final void initInferAttributes() | |
1375 | { | |
1376 | //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); | |
1377 | TypeFunction tf = type.toTypeFunction(); | |
1378 | if (tf.purity == PURE.impure) // purity not specified | |
7e7ebe3e | 1379 | purityInprocess = true; |
5fee5ec3 IB |
1380 | |
1381 | if (tf.trust == TRUST.default_) | |
7e7ebe3e | 1382 | safetyInprocess = true; |
5fee5ec3 IB |
1383 | |
1384 | if (!tf.isnothrow) | |
7e7ebe3e | 1385 | nothrowInprocess = true; |
5fee5ec3 IB |
1386 | |
1387 | if (!tf.isnogc) | |
7e7ebe3e | 1388 | nogcInprocess = true; |
5fee5ec3 | 1389 | |
235d5a96 | 1390 | if (!isVirtual() || this.isIntroducing()) |
7e7ebe3e | 1391 | returnInprocess = true; |
5fee5ec3 IB |
1392 | |
1393 | // Initialize for inferring STC.scope_ | |
7e7ebe3e | 1394 | inferScope = true; |
5fee5ec3 IB |
1395 | } |
1396 | ||
1397 | final PURE isPure() | |
1398 | { | |
1399 | //printf("FuncDeclaration::isPure() '%s'\n", toChars()); | |
1400 | TypeFunction tf = type.toTypeFunction(); | |
7e7ebe3e | 1401 | if (purityInprocess) |
5fee5ec3 IB |
1402 | setImpure(); |
1403 | if (tf.purity == PURE.fwdref) | |
1404 | tf.purityLevel(); | |
1405 | PURE purity = tf.purity; | |
1406 | if (purity > PURE.weak && isNested()) | |
1407 | purity = PURE.weak; | |
1408 | if (purity > PURE.weak && needThis()) | |
1409 | { | |
1410 | // The attribute of the 'this' reference affects purity strength | |
1411 | if (type.mod & MODFlags.immutable_) | |
1412 | { | |
1413 | } | |
1414 | else if (type.mod & (MODFlags.const_ | MODFlags.wild) && purity >= PURE.const_) | |
1415 | purity = PURE.const_; | |
1416 | else | |
1417 | purity = PURE.weak; | |
1418 | } | |
1419 | tf.purity = purity; | |
1420 | // ^ This rely on the current situation that every FuncDeclaration has a | |
1421 | // unique TypeFunction. | |
1422 | return purity; | |
1423 | } | |
1424 | ||
1425 | final PURE isPureBypassingInference() | |
1426 | { | |
7e7ebe3e | 1427 | if (purityInprocess) |
5fee5ec3 IB |
1428 | return PURE.fwdref; |
1429 | else | |
1430 | return isPure(); | |
1431 | } | |
1432 | ||
1433 | /************************************** | |
1434 | * The function is doing something impure, | |
1435 | * so mark it as impure. | |
1436 | * If there's a purity error, return true. | |
1437 | */ | |
1438 | extern (D) final bool setImpure() | |
1439 | { | |
7e7ebe3e | 1440 | if (purityInprocess) |
5fee5ec3 | 1441 | { |
7e7ebe3e | 1442 | purityInprocess = false; |
5fee5ec3 IB |
1443 | if (fes) |
1444 | fes.func.setImpure(); | |
1445 | } | |
1446 | else if (isPure()) | |
1447 | return true; | |
1448 | return false; | |
1449 | } | |
1450 | ||
7e7ebe3e IB |
1451 | extern (D) final uint flags() |
1452 | { | |
1453 | return bitFields; | |
1454 | } | |
1455 | ||
1456 | extern (D) final uint flags(uint f) | |
1457 | { | |
1458 | bitFields = f; | |
1459 | return bitFields; | |
1460 | } | |
1461 | ||
5fee5ec3 IB |
1462 | final bool isSafe() |
1463 | { | |
7e7ebe3e | 1464 | if (safetyInprocess) |
5fee5ec3 IB |
1465 | setUnsafe(); |
1466 | return type.toTypeFunction().trust == TRUST.safe; | |
1467 | } | |
1468 | ||
1469 | final bool isSafeBypassingInference() | |
1470 | { | |
7e7ebe3e | 1471 | return !(safetyInprocess) && isSafe(); |
5fee5ec3 IB |
1472 | } |
1473 | ||
1474 | final bool isTrusted() | |
1475 | { | |
7e7ebe3e | 1476 | if (safetyInprocess) |
5fee5ec3 IB |
1477 | setUnsafe(); |
1478 | return type.toTypeFunction().trust == TRUST.trusted; | |
1479 | } | |
1480 | ||
1481 | /************************************** | |
5eb9927a IB |
1482 | * The function is doing something unsafe, so mark it as unsafe. |
1483 | * | |
1484 | * Params: | |
1485 | * gag = surpress error message (used in escape.d) | |
1486 | * loc = location of error | |
1487 | * fmt = printf-style format string | |
1488 | * arg0 = (optional) argument for first %s format specifier | |
1489 | * arg1 = (optional) argument for second %s format specifier | |
445d8def | 1490 | * arg2 = (optional) argument for third %s format specifier |
5eb9927a | 1491 | * Returns: whether there's a safe error |
5fee5ec3 | 1492 | */ |
5eb9927a | 1493 | extern (D) final bool setUnsafe( |
445d8def IB |
1494 | bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, |
1495 | RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) | |
5fee5ec3 | 1496 | { |
7e7ebe3e | 1497 | if (safetyInprocess) |
5fee5ec3 | 1498 | { |
7e7ebe3e | 1499 | safetyInprocess = false; |
5fee5ec3 | 1500 | type.toTypeFunction().trust = TRUST.system; |
610d7898 | 1501 | if (fmt || arg0) |
445d8def | 1502 | safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); |
5eb9927a | 1503 | |
5fee5ec3 IB |
1504 | if (fes) |
1505 | fes.func.setUnsafe(); | |
1506 | } | |
1507 | else if (isSafe()) | |
5eb9927a IB |
1508 | { |
1509 | if (!gag && fmt) | |
445d8def | 1510 | .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); |
5eb9927a | 1511 | |
5fee5ec3 | 1512 | return true; |
5eb9927a | 1513 | } |
5fee5ec3 IB |
1514 | return false; |
1515 | } | |
1516 | ||
5eb9927a IB |
1517 | /************************************** |
1518 | * The function is calling `@system` function `f`, so mark it as unsafe. | |
1519 | * | |
1520 | * Params: | |
1521 | * f = function being called (needed for diagnostic of inferred functions) | |
1522 | * Returns: whether there's a safe error | |
1523 | */ | |
1524 | extern (D) final bool setUnsafeCall(FuncDeclaration f) | |
1525 | { | |
1526 | return setUnsafe(false, f.loc, null, f, null); | |
1527 | } | |
1528 | ||
5fee5ec3 IB |
1529 | final bool isNogc() |
1530 | { | |
1531 | //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); | |
7e7ebe3e | 1532 | if (nogcInprocess) |
5fee5ec3 IB |
1533 | setGC(); |
1534 | return type.toTypeFunction().isnogc; | |
1535 | } | |
1536 | ||
1537 | final bool isNogcBypassingInference() | |
1538 | { | |
7e7ebe3e | 1539 | return !nogcInprocess && isNogc(); |
5eb9927a IB |
1540 | } |
1541 | ||
5fee5ec3 IB |
1542 | /************************************** |
1543 | * The function is doing something that may allocate with the GC, | |
1544 | * so mark it as not nogc (not no-how). | |
1545 | * Returns: | |
1546 | * true if function is marked as @nogc, meaning a user error occurred | |
1547 | */ | |
1548 | extern (D) final bool setGC() | |
1549 | { | |
1550 | //printf("setGC() %s\n", toChars()); | |
7e7ebe3e | 1551 | if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) |
5fee5ec3 IB |
1552 | { |
1553 | this.semantic2(_scope); | |
1554 | this.semantic3(_scope); | |
1555 | } | |
1556 | ||
7e7ebe3e | 1557 | if (nogcInprocess) |
5fee5ec3 | 1558 | { |
7e7ebe3e | 1559 | nogcInprocess = false; |
5fee5ec3 IB |
1560 | type.toTypeFunction().isnogc = false; |
1561 | if (fes) | |
1562 | fes.func.setGC(); | |
1563 | } | |
1564 | else if (isNogc()) | |
1565 | return true; | |
1566 | return false; | |
1567 | } | |
1568 | ||
1569 | extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) | |
1570 | { | |
1571 | if (!global.params.vgc) | |
1572 | return; | |
1573 | ||
1574 | Module m = getModule(); | |
1575 | if (m && m.isRoot() && !inUnittest()) | |
1576 | { | |
1577 | message(loc, "vgc: %s", warn); | |
1578 | } | |
1579 | } | |
1580 | ||
1581 | /******************************************** | |
1582 | * See if pointers from function parameters, mutable globals, or uplevel functions | |
1583 | * could leak into return value. | |
1584 | * Returns: | |
1585 | * true if the function return value is isolated from | |
1586 | * any inputs to the function | |
1587 | */ | |
1588 | extern (D) final bool isReturnIsolated() | |
1589 | { | |
1590 | //printf("isReturnIsolated(this: %s)\n", this.toChars); | |
1591 | TypeFunction tf = type.toTypeFunction(); | |
1592 | assert(tf.next); | |
1593 | ||
1594 | Type treti = tf.next; | |
1595 | if (tf.isref) | |
1596 | return isTypeIsolatedIndirect(treti); // check influence from parameters | |
1597 | ||
1598 | return isTypeIsolated(treti); | |
1599 | } | |
1600 | ||
1601 | /******************** | |
1602 | * See if pointers from function parameters, mutable globals, or uplevel functions | |
1603 | * could leak into type `t`. | |
1604 | * Params: | |
1605 | * t = type to check if it is isolated | |
1606 | * Returns: | |
1607 | * true if `t` is isolated from | |
1608 | * any inputs to the function | |
1609 | */ | |
1610 | extern (D) final bool isTypeIsolated(Type t) | |
1611 | { | |
1612 | StringTable!Type parentTypes; | |
0fb57034 IB |
1613 | const uniqueTypeID = t.getUniqueID(); |
1614 | if (uniqueTypeID) | |
1615 | { | |
1616 | const cacheResultPtr = uniqueTypeID in isTypeIsolatedCache; | |
1617 | if (cacheResultPtr !is null) | |
1618 | return *cacheResultPtr; | |
1619 | ||
1620 | parentTypes._init(); | |
1621 | const isIsolated = isTypeIsolated(t, parentTypes); | |
1622 | isTypeIsolatedCache[uniqueTypeID] = isIsolated; | |
1623 | return isIsolated; | |
1624 | } | |
1625 | else | |
1626 | { | |
1627 | parentTypes._init(); | |
1628 | return isTypeIsolated(t, parentTypes); | |
1629 | } | |
5fee5ec3 IB |
1630 | } |
1631 | ||
1632 | ///ditto | |
1633 | extern (D) final bool isTypeIsolated(Type t, ref StringTable!Type parentTypes) | |
1634 | { | |
1635 | //printf("this: %s, isTypeIsolated(t: %s)\n", this.toChars(), t.toChars()); | |
1636 | ||
1637 | t = t.baseElemOf(); | |
1638 | switch (t.ty) | |
1639 | { | |
1640 | case Tarray: | |
1641 | case Tpointer: | |
1642 | return isTypeIsolatedIndirect(t.nextOf()); // go down one level | |
1643 | ||
1644 | case Taarray: | |
1645 | case Tclass: | |
1646 | return isTypeIsolatedIndirect(t); | |
1647 | ||
1648 | case Tstruct: | |
1649 | /* Drill down and check the struct's fields | |
1650 | */ | |
1651 | auto sym = t.toDsymbol(null).isStructDeclaration(); | |
1652 | const tName = t.toChars.toDString; | |
1653 | const entry = parentTypes.insert(tName, t); | |
1654 | if (entry == null) | |
1655 | { | |
1656 | //we've already seen this type in a parent, not isolated | |
1657 | return false; | |
1658 | } | |
1659 | foreach (v; sym.fields) | |
1660 | { | |
1661 | Type tmi = v.type.addMod(t.mod); | |
1662 | //printf("\tt = %s, v: %s, vtype: %s, tmi = %s\n", | |
1663 | // t.toChars(), v.toChars(), v.type.toChars(), tmi.toChars()); | |
1664 | if (!isTypeIsolated(tmi, parentTypes)) | |
1665 | return false; | |
1666 | } | |
1667 | return true; | |
1668 | ||
1669 | default: | |
1670 | return true; | |
1671 | } | |
1672 | } | |
1673 | ||
1674 | /******************************************** | |
1675 | * Params: | |
1676 | * t = type of object to test one level of indirection down | |
1677 | * Returns: | |
1678 | * true if an object typed `t` has no indirections | |
1679 | * which could have come from the function's parameters, mutable | |
1680 | * globals, or uplevel functions. | |
1681 | */ | |
1682 | private bool isTypeIsolatedIndirect(Type t) | |
1683 | { | |
1684 | //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars()); | |
1685 | assert(t); | |
1686 | ||
1687 | /* Since `t` is one level down from an indirection, it could pick | |
1688 | * up a reference to a mutable global or an outer function, so | |
1689 | * return false. | |
1690 | */ | |
1691 | if (!isPureBypassingInference() || isNested()) | |
1692 | return false; | |
1693 | ||
1694 | TypeFunction tf = type.toTypeFunction(); | |
1695 | ||
1696 | //printf("isTypeIsolatedIndirect(%s) t = %s\n", tf.toChars(), t.toChars()); | |
1697 | ||
1698 | foreach (i, fparam; tf.parameterList) | |
1699 | { | |
1700 | Type tp = fparam.type; | |
1701 | if (!tp) | |
1702 | continue; | |
1703 | ||
ec486b73 | 1704 | if (fparam.isLazy() || fparam.isReference()) |
5fee5ec3 IB |
1705 | { |
1706 | if (!traverseIndirections(tp, t)) | |
1707 | return false; | |
1708 | continue; | |
1709 | } | |
1710 | ||
1711 | /* Goes down one level of indirection, then calls traverseIndirection() on | |
1712 | * the result. | |
1713 | * Returns: | |
1714 | * true if t is isolated from tp | |
1715 | */ | |
1716 | static bool traverse(Type tp, Type t) | |
1717 | { | |
1718 | tp = tp.baseElemOf(); | |
1719 | switch (tp.ty) | |
1720 | { | |
1721 | case Tarray: | |
1722 | case Tpointer: | |
1723 | return traverseIndirections(tp.nextOf(), t); | |
1724 | ||
1725 | case Taarray: | |
1726 | case Tclass: | |
1727 | return traverseIndirections(tp, t); | |
1728 | ||
1729 | case Tstruct: | |
1730 | /* Drill down and check the struct's fields | |
1731 | */ | |
1732 | auto sym = tp.toDsymbol(null).isStructDeclaration(); | |
1733 | foreach (v; sym.fields) | |
1734 | { | |
1735 | Type tprmi = v.type.addMod(tp.mod); | |
1736 | //printf("\ttp = %s, tprmi = %s\n", tp.toChars(), tprmi.toChars()); | |
1737 | if (!traverse(tprmi, t)) | |
1738 | return false; | |
1739 | } | |
1740 | return true; | |
1741 | ||
1742 | default: | |
1743 | return true; | |
1744 | } | |
1745 | } | |
1746 | ||
1747 | if (!traverse(tp, t)) | |
1748 | return false; | |
1749 | } | |
1750 | // The 'this' reference is a parameter, too | |
1751 | if (AggregateDeclaration ad = isCtorDeclaration() ? null : isThis()) | |
1752 | { | |
1753 | Type tthis = ad.getType().addMod(tf.mod); | |
1754 | //printf("\ttthis = %s\n", tthis.toChars()); | |
1755 | if (!traverseIndirections(tthis, t)) | |
1756 | return false; | |
1757 | } | |
1758 | ||
1759 | return true; | |
1760 | } | |
1761 | ||
1762 | /**************************************** | |
1763 | * Determine if function needs a static frame pointer. | |
1764 | * Returns: | |
1765 | * `true` if function is really nested within other function. | |
1766 | * Contracts: | |
1767 | * If isNested() returns true, isThis() should return false, | |
1768 | * unless the function needs a dual-context pointer. | |
1769 | */ | |
1770 | bool isNested() const | |
1771 | { | |
1772 | auto f = toAliasFunc(); | |
1773 | //printf("\ttoParent2() = '%s'\n", f.toParent2().toChars()); | |
1774 | return ((f.storage_class & STC.static_) == 0) && | |
5eb9927a | 1775 | (f._linkage == LINK.d) && |
5fee5ec3 IB |
1776 | (f.toParent2().isFuncDeclaration() !is null || |
1777 | f.toParent2() !is f.toParentLocal()); | |
1778 | } | |
1779 | ||
1780 | /**************************************** | |
1781 | * Determine if function is a non-static member function | |
1782 | * that has an implicit 'this' expression. | |
1783 | * Returns: | |
1784 | * The aggregate it is a member of, or null. | |
1785 | * Contracts: | |
1786 | * Both isThis() and isNested() should return true if function needs a dual-context pointer, | |
1787 | * otherwise if isThis() returns true, isNested() should return false. | |
1788 | */ | |
1789 | override inout(AggregateDeclaration) isThis() inout | |
1790 | { | |
1791 | //printf("+FuncDeclaration::isThis() '%s'\n", toChars()); | |
1792 | auto ad = (storage_class & STC.static_) ? .objc.isThis(this) : isMemberLocal(); | |
1793 | //printf("-FuncDeclaration::isThis() %p\n", ad); | |
1794 | return ad; | |
1795 | } | |
1796 | ||
1797 | override final bool needThis() | |
1798 | { | |
1799 | //printf("FuncDeclaration::needThis() '%s'\n", toChars()); | |
1800 | return toAliasFunc().isThis() !is null; | |
1801 | } | |
1802 | ||
1803 | // Determine if a function is pedantically virtual | |
1804 | final bool isVirtualMethod() | |
1805 | { | |
1806 | if (toAliasFunc() != this) | |
1807 | return toAliasFunc().isVirtualMethod(); | |
1808 | ||
1809 | //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars()); | |
1810 | if (!isVirtual()) | |
1811 | return false; | |
1812 | // If it's a final method, and does not override anything, then it is not virtual | |
1813 | if (isFinalFunc() && foverrides.dim == 0) | |
1814 | { | |
1815 | return false; | |
1816 | } | |
1817 | return true; | |
1818 | } | |
1819 | ||
1820 | // Determine if function goes into virtual function pointer table | |
1821 | bool isVirtual() const | |
1822 | { | |
1823 | if (toAliasFunc() != this) | |
1824 | return toAliasFunc().isVirtual(); | |
1825 | ||
1826 | auto p = toParent(); | |
1827 | ||
1828 | if (!isMember || !p.isClassDeclaration) | |
1829 | return false; | |
1830 | ||
1831 | if (p.isClassDeclaration.classKind == ClassKind.objc) | |
1832 | return .objc.isVirtual(this); | |
1833 | ||
1834 | version (none) | |
1835 | { | |
1836 | printf("FuncDeclaration::isVirtual(%s)\n", toChars()); | |
1837 | printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), visibility == Visibility.Kind.private_, isCtorDeclaration(), linkage != LINK.d); | |
1838 | printf("result is %d\n", isMember() && !(isStatic() || visibility == Visibility.Kind.private_ || visibility == Visibility.Kind.package_) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc())); | |
1839 | } | |
1840 | return !(isStatic() || visibility.kind == Visibility.Kind.private_ || visibility.kind == Visibility.Kind.package_) && !(p.isInterfaceDeclaration() && isFinalFunc()); | |
1841 | } | |
1842 | ||
1843 | final bool isFinalFunc() const | |
1844 | { | |
1845 | if (toAliasFunc() != this) | |
1846 | return toAliasFunc().isFinalFunc(); | |
1847 | ||
1848 | version (none) | |
1849 | {{ | |
1850 | auto cd = toParent().isClassDeclaration(); | |
1851 | printf("FuncDeclaration::isFinalFunc(%s), %x\n", toChars(), Declaration.isFinal()); | |
1852 | printf("%p %d %d %d\n", isMember(), isStatic(), Declaration.isFinal(), ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.final_)); | |
1853 | printf("result is %d\n", isMember() && (Declaration.isFinal() || (cd !is null && cd.storage_class & STC.final_))); | |
1854 | if (cd) | |
1855 | printf("\tmember of %s\n", cd.toChars()); | |
1856 | }} | |
1857 | if (!isMember()) | |
1858 | return false; | |
1859 | if (Declaration.isFinal()) | |
1860 | return true; | |
1861 | auto cd = toParent().isClassDeclaration(); | |
1862 | return (cd !is null) && (cd.storage_class & STC.final_); | |
1863 | } | |
1864 | ||
1865 | bool addPreInvariant() | |
1866 | { | |
1867 | auto ad = isThis(); | |
1868 | ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; | |
235d5a96 | 1869 | return (ad && !(cd && cd.isCPPclass()) && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked()); |
5fee5ec3 IB |
1870 | } |
1871 | ||
1872 | bool addPostInvariant() | |
1873 | { | |
1874 | auto ad = isThis(); | |
1875 | ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; | |
235d5a96 | 1876 | return (ad && !(cd && cd.isCPPclass()) && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked()); |
5fee5ec3 IB |
1877 | } |
1878 | ||
1879 | override const(char)* kind() const | |
1880 | { | |
235d5a96 | 1881 | return this.isGenerated() ? "generated function" : "function"; |
5fee5ec3 IB |
1882 | } |
1883 | ||
1884 | /******************************************** | |
1885 | * Returns: | |
1886 | * true if there are no overloads of this function | |
1887 | */ | |
1888 | final bool isUnique() const | |
1889 | { | |
1890 | bool result = false; | |
1891 | overloadApply(cast() this, (Dsymbol s) | |
1892 | { | |
1893 | auto f = s.isFuncDeclaration(); | |
1894 | if (!f) | |
1895 | return 0; | |
1896 | if (result) | |
1897 | { | |
1898 | result = false; | |
1899 | return 1; // ambiguous, done | |
1900 | } | |
1901 | else | |
1902 | { | |
1903 | result = true; | |
1904 | return 0; | |
1905 | } | |
1906 | }); | |
1907 | return result; | |
1908 | } | |
1909 | ||
1910 | /********************************************* | |
1911 | * In the current function, we are calling 'this' function. | |
1912 | * 1. Check to see if the current function can call 'this' function, issue error if not. | |
1913 | * 2. If the current function is not the parent of 'this' function, then add | |
1914 | * the current function to the list of siblings of 'this' function. | |
1915 | * 3. If the current function is a literal, and it's accessing an uplevel scope, | |
1916 | * then mark it as a delegate. | |
1917 | * Returns true if error occurs. | |
1918 | */ | |
1919 | extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc) | |
1920 | { | |
1921 | //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); | |
1922 | ||
1923 | if (auto fld = this.isFuncLiteralDeclaration()) | |
1924 | { | |
1925 | if (fld.tok == TOK.reserved) | |
1926 | { | |
1927 | fld.tok = TOK.function_; | |
1928 | fld.vthis = null; | |
1929 | } | |
1930 | } | |
1931 | ||
1932 | if (!parent || parent == sc.parent) | |
1933 | return false; | |
1934 | if (ident == Id.require || ident == Id.ensure) | |
1935 | return false; | |
1936 | if (!isThis() && !isNested()) | |
1937 | return false; | |
1938 | ||
1939 | // The current function | |
1940 | FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); | |
1941 | if (!fdthis) | |
1942 | return false; // out of function scope | |
1943 | ||
1944 | Dsymbol p = toParentLocal(); | |
1945 | Dsymbol p2 = toParent2(); | |
1946 | ||
1947 | // Function literals from fdthis to p must be delegates | |
1948 | ensureStaticLinkTo(fdthis, p); | |
1949 | if (p != p2) | |
1950 | ensureStaticLinkTo(fdthis, p2); | |
1951 | ||
1952 | if (isNested()) | |
1953 | { | |
1954 | // The function that this function is in | |
1955 | bool checkEnclosing(FuncDeclaration fdv) | |
1956 | { | |
1957 | if (!fdv) | |
1958 | return false; | |
1959 | if (fdv == fdthis) | |
1960 | return false; | |
1961 | ||
1962 | //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); | |
1963 | //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); | |
1964 | //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); | |
1965 | ||
1966 | // Add this function to the list of those which called us | |
1967 | if (fdthis != this) | |
1968 | { | |
1969 | bool found = false; | |
1970 | for (size_t i = 0; i < siblingCallers.dim; ++i) | |
1971 | { | |
1972 | if (siblingCallers[i] == fdthis) | |
1973 | found = true; | |
1974 | } | |
1975 | if (!found) | |
1976 | { | |
b6df1132 | 1977 | //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); |
5fee5ec3 | 1978 | if (!sc.intypeof && !(sc.flags & SCOPE.compile)) |
b6df1132 | 1979 | { |
5fee5ec3 | 1980 | siblingCallers.push(fdthis); |
b6df1132 | 1981 | } |
5fee5ec3 IB |
1982 | } |
1983 | } | |
1984 | ||
1985 | const lv = fdthis.getLevelAndCheck(loc, sc, fdv, this); | |
1986 | if (lv == LevelError) | |
1987 | return true; // error | |
1988 | if (lv == -1) | |
1989 | return false; // downlevel call | |
1990 | if (lv == 0) | |
1991 | return false; // same level call | |
1992 | ||
1993 | return false; // Uplevel call | |
1994 | } | |
1995 | ||
1996 | if (checkEnclosing(p.isFuncDeclaration())) | |
1997 | return true; | |
1998 | if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) | |
1999 | return true; | |
2000 | } | |
2001 | return false; | |
2002 | } | |
2003 | ||
2004 | /******************************* | |
2005 | * Look at all the variables in this function that are referenced | |
2006 | * by nested functions, and determine if a closure needs to be | |
2007 | * created for them. | |
2008 | */ | |
2009 | final bool needsClosure() | |
2010 | { | |
2011 | /* Need a closure for all the closureVars[] if any of the | |
2012 | * closureVars[] are accessed by a | |
2013 | * function that escapes the scope of this function. | |
2014 | * We take the conservative approach and decide that a function needs | |
2015 | * a closure if it: | |
2016 | * 1) is a virtual function | |
2017 | * 2) has its address taken | |
2018 | * 3) has a parent that escapes | |
2019 | * 4) calls another nested function that needs a closure | |
2020 | * | |
2021 | * Note that since a non-virtual function can be called by | |
2022 | * a virtual one, if that non-virtual function accesses a closure | |
2023 | * var, the closure still has to be taken. Hence, we check for isThis() | |
2024 | * instead of isVirtual(). (thanks to David Friedman) | |
2025 | * | |
2026 | * When the function returns a local struct or class, `requiresClosure` | |
2027 | * is already set to `true` upon entering this function when the | |
2028 | * struct/class refers to a local variable and a closure is needed. | |
2029 | */ | |
2030 | ||
2031 | //printf("FuncDeclaration::needsClosure() %s\n", toChars()); | |
2032 | ||
2033 | if (requiresClosure) | |
2034 | goto Lyes; | |
2035 | ||
2036 | for (size_t i = 0; i < closureVars.dim; i++) | |
2037 | { | |
2038 | VarDeclaration v = closureVars[i]; | |
2039 | //printf("\tv = %s\n", v.toChars()); | |
2040 | ||
2041 | for (size_t j = 0; j < v.nestedrefs.dim; j++) | |
2042 | { | |
2043 | FuncDeclaration f = v.nestedrefs[j]; | |
2044 | assert(f != this); | |
2045 | ||
2046 | /* __require and __ensure will always get called directly, | |
2047 | * so they never make outer functions closure. | |
2048 | */ | |
2049 | if (f.ident == Id.require || f.ident == Id.ensure) | |
2050 | continue; | |
2051 | ||
2052 | //printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf); | |
2053 | ||
2054 | /* Look to see if f escapes. We consider all parents of f within | |
2055 | * this, and also all siblings which call f; if any of them escape, | |
2056 | * so does f. | |
2057 | * Mark all affected functions as requiring closures. | |
2058 | */ | |
2059 | for (Dsymbol s = f; s && s != this; s = s.toParentP(this)) | |
2060 | { | |
2061 | FuncDeclaration fx = s.isFuncDeclaration(); | |
2062 | if (!fx) | |
2063 | continue; | |
2064 | if (fx.isThis() || fx.tookAddressOf) | |
2065 | { | |
2066 | //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf); | |
2067 | ||
2068 | /* Mark as needing closure any functions between this and f | |
2069 | */ | |
2070 | markAsNeedingClosure((fx == f) ? fx.toParentP(this) : fx, this); | |
2071 | ||
2072 | requiresClosure = true; | |
2073 | } | |
2074 | ||
2075 | /* We also need to check if any sibling functions that | |
2076 | * called us, have escaped. This is recursive: we need | |
2077 | * to check the callers of our siblings. | |
2078 | */ | |
2079 | if (checkEscapingSiblings(fx, this)) | |
2080 | requiresClosure = true; | |
2081 | ||
2082 | /* https://issues.dlang.org/show_bug.cgi?id=12406 | |
2083 | * Iterate all closureVars to mark all descendant | |
2084 | * nested functions that access to the closing context of this function. | |
2085 | */ | |
2086 | } | |
2087 | } | |
2088 | } | |
2089 | if (requiresClosure) | |
2090 | goto Lyes; | |
2091 | ||
2092 | return false; | |
2093 | ||
2094 | Lyes: | |
5fee5ec3 IB |
2095 | return true; |
2096 | } | |
2097 | ||
2098 | /*********************************************** | |
2099 | * Check that the function contains any closure. | |
2100 | * If it's @nogc, report suitable errors. | |
2101 | * This is mostly consistent with FuncDeclaration::needsClosure(). | |
2102 | * | |
2103 | * Returns: | |
2104 | * true if any errors occur. | |
2105 | */ | |
b6df1132 | 2106 | extern (C++) final bool checkClosure() |
5fee5ec3 | 2107 | { |
b6df1132 | 2108 | //printf("checkClosure() %s\n", toChars()); |
5fee5ec3 IB |
2109 | if (!needsClosure()) |
2110 | return false; | |
2111 | ||
2112 | if (setGC()) | |
2113 | { | |
b6df1132 IB |
2114 | error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars()); |
2115 | if (global.gag) // need not report supplemental errors | |
2116 | return true; | |
2117 | } | |
2118 | else if (global.params.betterC) | |
2119 | { | |
2120 | error("is `-betterC` yet allocates closure for `%s()` with the GC", toChars()); | |
5fee5ec3 IB |
2121 | if (global.gag) // need not report supplemental errors |
2122 | return true; | |
2123 | } | |
2124 | else | |
2125 | { | |
2126 | printGCUsage(loc, "using closure causes GC allocation"); | |
2127 | return false; | |
2128 | } | |
2129 | ||
2130 | FuncDeclarations a; | |
2131 | foreach (v; closureVars) | |
2132 | { | |
2133 | foreach (f; v.nestedrefs) | |
2134 | { | |
2135 | assert(f !is this); | |
2136 | ||
2137 | LcheckAncestorsOfANestedRef: | |
2138 | for (Dsymbol s = f; s && s !is this; s = s.toParentP(this)) | |
2139 | { | |
2140 | auto fx = s.isFuncDeclaration(); | |
2141 | if (!fx) | |
2142 | continue; | |
2143 | if (fx.isThis() || | |
2144 | fx.tookAddressOf || | |
2145 | checkEscapingSiblings(fx, this)) | |
2146 | { | |
2147 | foreach (f2; a) | |
2148 | { | |
2149 | if (f2 == f) | |
2150 | break LcheckAncestorsOfANestedRef; | |
2151 | } | |
2152 | a.push(f); | |
b6df1132 | 2153 | .errorSupplemental(f.loc, "`%s` closes over variable `%s` at %s", |
5fee5ec3 IB |
2154 | f.toPrettyChars(), v.toChars(), v.loc.toChars()); |
2155 | break LcheckAncestorsOfANestedRef; | |
2156 | } | |
2157 | } | |
2158 | } | |
2159 | } | |
2160 | ||
2161 | return true; | |
2162 | } | |
2163 | ||
2164 | /*********************************************** | |
2165 | * Determine if function's variables are referenced by a function | |
2166 | * nested within it. | |
2167 | */ | |
2168 | final bool hasNestedFrameRefs() | |
2169 | { | |
2170 | if (closureVars.dim) | |
2171 | return true; | |
2172 | ||
2173 | /* If a virtual function has contracts, assume its variables are referenced | |
2174 | * by those contracts, even if they aren't. Because they might be referenced | |
2175 | * by the overridden or overriding function's contracts. | |
2176 | * This can happen because frequire and fensure are implemented as nested functions, | |
2177 | * and they can be called directly by an overriding function and the overriding function's | |
2178 | * context had better match, or | |
2179 | * https://issues.dlang.org/show_bug.cgi?id=7335 will bite. | |
2180 | */ | |
2181 | if (fdrequire || fdensure) | |
2182 | return true; | |
2183 | ||
2184 | if (foverrides.dim && isVirtualMethod()) | |
2185 | { | |
2186 | for (size_t i = 0; i < foverrides.dim; i++) | |
2187 | { | |
2188 | FuncDeclaration fdv = foverrides[i]; | |
2189 | if (fdv.hasNestedFrameRefs()) | |
2190 | return true; | |
2191 | } | |
2192 | } | |
2193 | return false; | |
2194 | } | |
2195 | ||
2196 | /**************************************************** | |
2197 | * Check whether result variable can be built. | |
2198 | * Returns: | |
2199 | * `true` if the function has a return type that | |
2200 | * is different from `void`. | |
2201 | */ | |
2202 | extern (D) private bool canBuildResultVar() | |
2203 | { | |
2204 | auto f = cast(TypeFunction)type; | |
2205 | return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; | |
2206 | } | |
2207 | ||
2208 | /**************************************************** | |
2209 | * Declare result variable lazily. | |
2210 | */ | |
2211 | extern (D) final void buildResultVar(Scope* sc, Type tret) | |
2212 | { | |
2213 | if (!vresult) | |
2214 | { | |
2215 | Loc loc = fensure ? fensure.loc : this.loc; | |
2216 | ||
2217 | /* If inferRetType is true, tret may not be a correct return type yet. | |
2218 | * So, in here it may be a temporary type for vresult, and after | |
2219 | * fbody.dsymbolSemantic() running, vresult.type might be modified. | |
2220 | */ | |
2221 | vresult = new VarDeclaration(loc, tret, Id.result, null); | |
2222 | vresult.storage_class |= STC.nodtor | STC.temp; | |
2223 | if (!isVirtual()) | |
2224 | vresult.storage_class |= STC.const_; | |
2225 | vresult.storage_class |= STC.result; | |
2226 | ||
2227 | // set before the semantic() for checkNestedReference() | |
2228 | vresult.parent = this; | |
2229 | } | |
2230 | ||
235d5a96 | 2231 | if (sc && vresult.semanticRun == PASS.initial) |
5fee5ec3 IB |
2232 | { |
2233 | TypeFunction tf = type.toTypeFunction(); | |
2234 | if (tf.isref) | |
2235 | vresult.storage_class |= STC.ref_; | |
2236 | vresult.type = tret; | |
2237 | ||
2238 | vresult.dsymbolSemantic(sc); | |
2239 | ||
2240 | if (!sc.insert(vresult)) | |
2241 | error("out result %s is already defined", vresult.toChars()); | |
2242 | assert(vresult.parent == this); | |
2243 | } | |
2244 | } | |
2245 | ||
2246 | /**************************************************** | |
2247 | * Merge into this function the 'in' contracts of all it overrides. | |
2248 | * 'in's are OR'd together, i.e. only one of them needs to pass. | |
2249 | */ | |
2250 | extern (D) final Statement mergeFrequire(Statement sf, Expressions* params) | |
2251 | { | |
2252 | /* If a base function and its override both have an IN contract, then | |
2253 | * only one of them needs to succeed. This is done by generating: | |
2254 | * | |
2255 | * void derived.in() { | |
2256 | * try { | |
2257 | * base.in(); | |
2258 | * } | |
2259 | * catch () { | |
2260 | * ... body of derived.in() ... | |
2261 | * } | |
2262 | * } | |
2263 | * | |
2264 | * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. | |
2265 | * If base.in() throws, then derived.in()'s body is executed. | |
2266 | */ | |
2267 | ||
2268 | foreach (fdv; foverrides) | |
2269 | { | |
2270 | /* The semantic pass on the contracts of the overridden functions must | |
2271 | * be completed before code generation occurs. | |
2272 | * https://issues.dlang.org/show_bug.cgi?id=3602 | |
2273 | */ | |
2274 | if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) | |
2275 | { | |
2276 | assert(fdv._scope); | |
2277 | Scope* sc = fdv._scope.push(); | |
2278 | sc.stc &= ~STC.override_; | |
2279 | fdv.semantic3(sc); | |
2280 | sc.pop(); | |
2281 | } | |
2282 | ||
2283 | sf = fdv.mergeFrequire(sf, params); | |
2284 | if (!sf || !fdv.fdrequire) | |
2285 | return null; | |
2286 | //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); | |
2287 | /* Make the call: | |
2288 | * try { __require(params); } | |
2289 | * catch (Throwable) { frequire; } | |
2290 | */ | |
2291 | params = Expression.arraySyntaxCopy(params); | |
2292 | Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); | |
2293 | Statement s2 = new ExpStatement(loc, e); | |
2294 | ||
2295 | auto c = new Catch(loc, getThrowable(), null, sf); | |
2296 | c.internalCatch = true; | |
2297 | auto catches = new Catches(); | |
2298 | catches.push(c); | |
2299 | sf = new TryCatchStatement(loc, s2, catches); | |
2300 | } | |
2301 | return sf; | |
2302 | } | |
2303 | ||
2304 | /**************************************************** | |
2305 | * Merge into this function the 'in' contracts of all it overrides. | |
2306 | */ | |
2307 | extern (D) final Statement mergeFrequireInclusivePreview(Statement sf, Expressions* params) | |
2308 | { | |
2309 | /* If a base function and its override both have an IN contract, then | |
2310 | * the override in contract must widen the guarantee of the base contract. | |
2311 | * This is checked by generating: | |
2312 | * | |
2313 | * void derived.in() { | |
2314 | * try { | |
2315 | * ... body of derived.in() ... | |
2316 | * } | |
2317 | * catch () { | |
2318 | * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract. | |
2319 | * base.in(); | |
2320 | * assert(false, "Logic error: " ~ thr.msg); | |
2321 | * } | |
2322 | */ | |
2323 | ||
2324 | foreach (fdv; foverrides) | |
2325 | { | |
2326 | /* The semantic pass on the contracts of the overridden functions must | |
2327 | * be completed before code generation occurs. | |
2328 | * https://issues.dlang.org/show_bug.cgi?id=3602 | |
2329 | */ | |
2330 | if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) | |
2331 | { | |
2332 | assert(fdv._scope); | |
2333 | Scope* sc = fdv._scope.push(); | |
2334 | sc.stc &= ~STC.override_; | |
2335 | fdv.semantic3(sc); | |
2336 | sc.pop(); | |
2337 | } | |
2338 | ||
2339 | sf = fdv.mergeFrequireInclusivePreview(sf, params); | |
2340 | if (sf && fdv.fdrequire) | |
2341 | { | |
2342 | const loc = this.fdrequire.loc; | |
2343 | ||
2344 | //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); | |
2345 | /* Make the call: | |
2346 | * try { frequire; } | |
2347 | * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } | |
2348 | */ | |
2349 | Identifier id = Identifier.generateId("thr"); | |
2350 | params = Expression.arraySyntaxCopy(params); | |
2351 | Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); | |
2352 | Statement s2 = new ExpStatement(loc, e); | |
2353 | // assert(false, ...) | |
2354 | // TODO make this a runtime helper to allow: | |
2355 | // - chaining the original expression | |
2356 | // - nogc concatenation | |
2357 | Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); | |
2358 | Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); | |
2359 | ||
2360 | Statement s3 = new CompoundStatement(loc, s2, fail); | |
2361 | ||
2362 | auto c = new Catch(loc, getThrowable(), id, s3); | |
2363 | c.internalCatch = true; | |
2364 | auto catches = new Catches(); | |
2365 | catches.push(c); | |
2366 | sf = new TryCatchStatement(loc, sf, catches); | |
2367 | } | |
2368 | else | |
2369 | return null; | |
2370 | } | |
2371 | return sf; | |
2372 | } | |
2373 | ||
2374 | /**************************************************** | |
2375 | * Determine whether an 'out' contract is declared inside | |
2376 | * the given function or any of its overrides. | |
2377 | * Params: | |
2378 | * fd = the function to search | |
2379 | * Returns: | |
2380 | * true found an 'out' contract | |
2381 | */ | |
2382 | static bool needsFensure(FuncDeclaration fd) | |
2383 | { | |
2384 | if (fd.fensures) | |
2385 | return true; | |
2386 | ||
2387 | foreach (fdv; fd.foverrides) | |
2388 | { | |
2389 | if (needsFensure(fdv)) | |
2390 | return true; | |
2391 | } | |
2392 | return false; | |
2393 | } | |
2394 | ||
2395 | /**************************************************** | |
2396 | * Rewrite contracts as statements. | |
2397 | */ | |
2398 | final void buildEnsureRequire() | |
2399 | { | |
2400 | ||
2401 | if (frequires) | |
2402 | { | |
2403 | /* in { statements1... } | |
2404 | * in { statements2... } | |
2405 | * ... | |
2406 | * becomes: | |
2407 | * in { { statements1... } { statements2... } ... } | |
2408 | */ | |
2409 | assert(frequires.dim); | |
2410 | auto loc = (*frequires)[0].loc; | |
2411 | auto s = new Statements; | |
2412 | foreach (r; *frequires) | |
2413 | { | |
2414 | s.push(new ScopeStatement(r.loc, r, r.loc)); | |
2415 | } | |
2416 | frequire = new CompoundStatement(loc, s); | |
2417 | } | |
2418 | ||
2419 | if (fensures) | |
2420 | { | |
2421 | /* out(id1) { statements1... } | |
2422 | * out(id2) { statements2... } | |
2423 | * ... | |
2424 | * becomes: | |
2425 | * out(__result) { { ref id1 = __result; { statements1... } } | |
2426 | * { ref id2 = __result; { statements2... } } ... } | |
2427 | */ | |
2428 | assert(fensures.dim); | |
2429 | auto loc = (*fensures)[0].ensure.loc; | |
2430 | auto s = new Statements; | |
2431 | foreach (r; *fensures) | |
2432 | { | |
2433 | if (r.id && canBuildResultVar()) | |
2434 | { | |
2435 | auto rloc = r.ensure.loc; | |
2436 | auto resultId = new IdentifierExp(rloc, Id.result); | |
2437 | auto init = new ExpInitializer(rloc, resultId); | |
2438 | auto stc = STC.ref_ | STC.temp | STC.result; | |
2439 | auto decl = new VarDeclaration(rloc, null, r.id, init, stc); | |
2440 | auto sdecl = new ExpStatement(rloc, decl); | |
2441 | s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); | |
2442 | } | |
2443 | else | |
2444 | { | |
2445 | s.push(r.ensure); | |
2446 | } | |
2447 | } | |
2448 | fensure = new CompoundStatement(loc, s); | |
2449 | } | |
2450 | ||
2451 | if (!isVirtual()) | |
2452 | return; | |
2453 | ||
2454 | /* Rewrite contracts as nested functions, then call them. Doing it as nested | |
2455 | * functions means that overriding functions can call them. | |
2456 | */ | |
2457 | TypeFunction f = cast(TypeFunction) type; | |
2458 | ||
2459 | /* Make a copy of the parameters and make them all ref */ | |
2460 | static Parameters* toRefCopy(ParameterList parameterList) | |
2461 | { | |
2462 | auto result = new Parameters(); | |
2463 | ||
2464 | foreach (n, p; parameterList) | |
2465 | { | |
2466 | p = p.syntaxCopy(); | |
ec486b73 | 2467 | if (!p.isLazy()) |
5fee5ec3 IB |
2468 | p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; |
2469 | p.defaultArg = null; // won't be the same with ref | |
2470 | result.push(p); | |
2471 | } | |
2472 | ||
2473 | return result; | |
2474 | } | |
2475 | ||
2476 | if (frequire) | |
2477 | { | |
2478 | /* in { ... } | |
2479 | * becomes: | |
2480 | * void __require(ref params) { ... } | |
2481 | * __require(params); | |
2482 | */ | |
2483 | Loc loc = frequire.loc; | |
2484 | fdrequireParams = new Expressions(); | |
2485 | if (parameters) | |
2486 | { | |
2487 | foreach (vd; *parameters) | |
2488 | fdrequireParams.push(new VarExp(loc, vd)); | |
2489 | } | |
2490 | auto fo = cast(TypeFunction)(originalType ? originalType : f); | |
2491 | auto fparams = toRefCopy(fo.parameterList); | |
2492 | auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); | |
2493 | tf.isnothrow = f.isnothrow; | |
2494 | tf.isnogc = f.isnogc; | |
2495 | tf.purity = f.purity; | |
2496 | tf.trust = f.trust; | |
2497 | auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); | |
2498 | fd.fbody = frequire; | |
2499 | Statement s1 = new ExpStatement(loc, fd); | |
2500 | Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams); | |
2501 | Statement s2 = new ExpStatement(loc, e); | |
2502 | frequire = new CompoundStatement(loc, s1, s2); | |
2503 | fdrequire = fd; | |
2504 | } | |
2505 | ||
2506 | /* We need to set fdensureParams here and not in the block below to | |
2507 | * have the parameters available when calling a base class ensure(), | |
2508 | * even if this function doesn't have an out contract. | |
2509 | */ | |
2510 | fdensureParams = new Expressions(); | |
2511 | if (canBuildResultVar()) | |
2512 | fdensureParams.push(new IdentifierExp(loc, Id.result)); | |
2513 | if (parameters) | |
2514 | { | |
2515 | foreach (vd; *parameters) | |
2516 | fdensureParams.push(new VarExp(loc, vd)); | |
2517 | } | |
2518 | ||
2519 | if (fensure) | |
2520 | { | |
2521 | /* out (result) { ... } | |
2522 | * becomes: | |
2523 | * void __ensure(ref tret result, ref params) { ... } | |
2524 | * __ensure(result, params); | |
2525 | */ | |
2526 | Loc loc = fensure.loc; | |
2527 | auto fparams = new Parameters(); | |
2528 | if (canBuildResultVar()) | |
2529 | { | |
2530 | Parameter p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); | |
2531 | fparams.push(p); | |
2532 | } | |
2533 | auto fo = cast(TypeFunction)(originalType ? originalType : f); | |
2534 | fparams.pushSlice((*toRefCopy(fo.parameterList))[]); | |
2535 | auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); | |
2536 | tf.isnothrow = f.isnothrow; | |
2537 | tf.isnogc = f.isnogc; | |
2538 | tf.purity = f.purity; | |
2539 | tf.trust = f.trust; | |
2540 | auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); | |
2541 | fd.fbody = fensure; | |
2542 | Statement s1 = new ExpStatement(loc, fd); | |
2543 | Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams); | |
2544 | Statement s2 = new ExpStatement(loc, e); | |
2545 | fensure = new CompoundStatement(loc, s1, s2); | |
2546 | fdensure = fd; | |
2547 | } | |
2548 | } | |
2549 | ||
2550 | /**************************************************** | |
2551 | * Merge into this function the 'out' contracts of all it overrides. | |
2552 | * 'out's are AND'd together, i.e. all of them need to pass. | |
2553 | */ | |
2554 | extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params) | |
2555 | { | |
2556 | /* Same comments as for mergeFrequire(), except that we take care | |
2557 | * of generating a consistent reference to the 'result' local by | |
2558 | * explicitly passing 'result' to the nested function as a reference | |
2559 | * argument. | |
2560 | * This won't work for the 'this' parameter as it would require changing | |
2561 | * the semantic code for the nested function so that it looks on the parameter | |
2562 | * list for the 'this' pointer, something that would need an unknown amount | |
2563 | * of tweaking of various parts of the compiler that I'd rather leave alone. | |
2564 | */ | |
2565 | foreach (fdv; foverrides) | |
2566 | { | |
2567 | /* The semantic pass on the contracts of the overridden functions must | |
2568 | * be completed before code generation occurs. | |
2569 | * https://issues.dlang.org/show_bug.cgi?id=3602 and | |
2570 | * https://issues.dlang.org/show_bug.cgi?id=5230 | |
2571 | */ | |
2572 | if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) | |
2573 | { | |
2574 | assert(fdv._scope); | |
2575 | Scope* sc = fdv._scope.push(); | |
2576 | sc.stc &= ~STC.override_; | |
2577 | fdv.semantic3(sc); | |
2578 | sc.pop(); | |
2579 | } | |
2580 | ||
2581 | sf = fdv.mergeFensure(sf, oid, params); | |
2582 | if (fdv.fdensure) | |
2583 | { | |
2584 | //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); | |
2585 | // Make the call: __ensure(result, params) | |
2586 | params = Expression.arraySyntaxCopy(params); | |
2587 | if (canBuildResultVar()) | |
2588 | { | |
2589 | Type t1 = fdv.type.nextOf().toBasetype(); | |
2590 | Type t2 = this.type.nextOf().toBasetype(); | |
2591 | if (t1.isBaseOf(t2, null)) | |
2592 | { | |
2593 | /* Making temporary reference variable is necessary | |
2594 | * in covariant return. | |
2595 | * https://issues.dlang.org/show_bug.cgi?id=5204 | |
2596 | * https://issues.dlang.org/show_bug.cgi?id=10479 | |
2597 | */ | |
2598 | Expression* eresult = &(*params)[0]; | |
2599 | auto ei = new ExpInitializer(Loc.initial, *eresult); | |
2600 | auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); | |
2601 | v.storage_class |= STC.temp; | |
2602 | auto de = new DeclarationExp(Loc.initial, v); | |
2603 | auto ve = new VarExp(Loc.initial, v); | |
2604 | *eresult = new CommaExp(Loc.initial, de, ve); | |
2605 | } | |
2606 | } | |
2607 | Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params); | |
2608 | Statement s2 = new ExpStatement(loc, e); | |
2609 | ||
2610 | if (sf) | |
2611 | { | |
2612 | sf = new CompoundStatement(sf.loc, s2, sf); | |
2613 | } | |
2614 | else | |
2615 | sf = s2; | |
2616 | } | |
2617 | } | |
2618 | return sf; | |
2619 | } | |
2620 | ||
2621 | /********************************************* | |
2622 | * Returns: the function's parameter list, and whether | |
2623 | * it is variadic or not. | |
2624 | */ | |
2625 | final ParameterList getParameterList() | |
2626 | { | |
2627 | if (type) | |
2628 | { | |
2629 | TypeFunction fdtype = type.isTypeFunction(); | |
2630 | if (fdtype) // Could also be TypeError | |
2631 | return fdtype.parameterList; | |
2632 | } | |
2633 | ||
2634 | return ParameterList(null, VarArg.none); | |
2635 | } | |
2636 | ||
2637 | /********************************** | |
2638 | * Generate a FuncDeclaration for a runtime library function. | |
2639 | */ | |
2640 | static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0) | |
2641 | { | |
2642 | return genCfunc(fparams, treturn, Identifier.idPool(name, cast(uint)strlen(name)), stc); | |
2643 | } | |
2644 | ||
2645 | static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0) | |
2646 | { | |
2647 | FuncDeclaration fd; | |
2648 | TypeFunction tf; | |
2649 | Dsymbol s; | |
2650 | __gshared DsymbolTable st = null; | |
2651 | ||
2652 | //printf("genCfunc(name = '%s')\n", id.toChars()); | |
2653 | //printf("treturn\n\t"); treturn.print(); | |
2654 | ||
2655 | // See if already in table | |
2656 | if (!st) | |
2657 | st = new DsymbolTable(); | |
2658 | s = st.lookup(id); | |
2659 | if (s) | |
2660 | { | |
2661 | fd = s.isFuncDeclaration(); | |
2662 | assert(fd); | |
2663 | assert(fd.type.nextOf().equals(treturn)); | |
2664 | } | |
2665 | else | |
2666 | { | |
2667 | tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc); | |
2668 | fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf); | |
2669 | fd.visibility = Visibility(Visibility.Kind.public_); | |
5eb9927a | 2670 | fd._linkage = LINK.c; |
5fee5ec3 IB |
2671 | |
2672 | st.insert(fd); | |
2673 | } | |
2674 | return fd; | |
2675 | } | |
2676 | ||
235d5a96 IB |
2677 | /+ |
2678 | + Checks the parameter and return types iff this is a `main` function. | |
2679 | + | |
2680 | + The following signatures are allowed for a `D main`: | |
2681 | + - Either no or a single parameter of type `string[]` | |
2682 | + - Return type is either `void`, `int` or `noreturn` | |
2683 | + | |
2684 | + The following signatures are standard C: | |
2685 | + - `int main()` | |
2686 | + - `int main(int, char**)` | |
2687 | + | |
2688 | + This function accepts the following non-standard extensions: | |
2689 | + - `char** envp` as a third parameter | |
2690 | + - `void` / `noreturn` as return type | |
2691 | + | |
2692 | + This function will issue errors for unexpected arguments / return types. | |
2693 | +/ | |
2694 | extern (D) final void checkMain() | |
2695 | { | |
2696 | if (ident != Id.main || isMember() || isNested()) | |
2697 | return; // Not a main function | |
2698 | ||
5fee5ec3 | 2699 | TypeFunction tf = type.toTypeFunction(); |
235d5a96 IB |
2700 | |
2701 | Type retType = tf.nextOf(); | |
2702 | if (!retType) | |
2703 | { | |
2704 | // auto main(), check after semantic | |
2705 | assert(this.inferRetType); | |
2706 | return; | |
2707 | } | |
2708 | ||
2709 | /// Checks whether `t` is equivalent to `char**` | |
2710 | /// Ignores qualifiers and treats enums according to their base type | |
2711 | static bool isCharPtrPtr(Type t) | |
2712 | { | |
2713 | auto tp = t.toBasetype().isTypePointer(); | |
2714 | if (!tp) | |
2715 | return false; | |
2716 | ||
2717 | tp = tp.next.toBasetype().isTypePointer(); | |
2718 | if (!tp) | |
2719 | return false; | |
2720 | ||
2721 | return tp.next.toBasetype().ty == Tchar; | |
2722 | } | |
2723 | ||
2724 | // Neither of these qualifiers is allowed because they affect the ABI | |
2725 | enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_; | |
2726 | ||
5fee5ec3 IB |
2727 | const nparams = tf.parameterList.length; |
2728 | bool argerr; | |
235d5a96 | 2729 | |
5eb9927a | 2730 | const linkage = resolvedLinkage(); |
235d5a96 IB |
2731 | if (linkage == LINK.d) |
2732 | { | |
2733 | if (nparams == 1) | |
5fee5ec3 | 2734 | { |
235d5a96 IB |
2735 | auto fparam0 = tf.parameterList[0]; |
2736 | auto t = fparam0.type.toBasetype(); | |
2737 | if (t.ty != Tarray || | |
2738 | t.nextOf().ty != Tarray || | |
2739 | t.nextOf().nextOf().ty != Tchar || | |
2740 | fparam0.storageClass & invalidSTC) | |
2741 | { | |
2742 | argerr = true; | |
2743 | } | |
5fee5ec3 | 2744 | } |
235d5a96 IB |
2745 | |
2746 | if (tf.parameterList.varargs || nparams >= 2 || argerr) | |
2747 | error("parameter list must be empty or accept one parameter of type `string[]`"); | |
5fee5ec3 IB |
2748 | } |
2749 | ||
235d5a96 IB |
2750 | else if (linkage == LINK.c) |
2751 | { | |
2752 | if (nparams == 2 || nparams == 3) | |
2753 | { | |
2754 | // Argument count must be int | |
2755 | auto argCount = tf.parameterList[0]; | |
2756 | argerr |= !!(argCount.storageClass & invalidSTC); | |
2757 | argerr |= argCount.type.toBasetype().ty != Tint32; | |
2758 | ||
2759 | // Argument pointer must be char** | |
2760 | auto argPtr = tf.parameterList[1]; | |
2761 | argerr |= !!(argPtr.storageClass & invalidSTC); | |
2762 | argerr |= !isCharPtrPtr(argPtr.type); | |
2763 | ||
2764 | // `char** environ` is a common extension, see J.5.1 of the C standard | |
2765 | if (nparams == 3) | |
2766 | { | |
2767 | auto envPtr = tf.parameterList[2]; | |
2768 | argerr |= !!(envPtr.storageClass & invalidSTC); | |
2769 | argerr |= !isCharPtrPtr(envPtr.type); | |
2770 | } | |
2771 | } | |
2772 | else | |
2773 | argerr = nparams != 0; | |
2774 | ||
2775 | // Disallow variadic main() - except for K&R declarations in C files. | |
2776 | // E.g. int main(), int main(argc, argv) int argc, char** argc { ... } | |
2777 | if (tf.parameterList.varargs && (!this.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams))) | |
2778 | argerr |= true; | |
2779 | ||
2780 | if (argerr) | |
2781 | { | |
2782 | error("parameters must match one of the following signatures"); | |
2783 | loc.errorSupplemental("`main()`"); | |
2784 | loc.errorSupplemental("`main(int argc, char** argv)`"); | |
2785 | loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]"); | |
2786 | } | |
2787 | } | |
2788 | else | |
2789 | return; // Neither C nor D main, ignore (should probably be an error) | |
2790 | ||
2791 | // Allow enums with appropriate base types (same ABI) | |
2792 | retType = retType.toBasetype(); | |
2793 | ||
2794 | if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn) | |
0fb57034 | 2795 | error("must return `int`, `void` or `noreturn`, not `%s`", tf.nextOf().toChars()); |
5fee5ec3 IB |
2796 | } |
2797 | ||
2798 | /*********************************************** | |
2799 | * Check all return statements for a function to verify that returning | |
2800 | * using NRVO is possible. | |
2801 | * | |
2802 | * Returns: | |
2803 | * `false` if the result cannot be returned by hidden reference. | |
2804 | */ | |
2805 | final bool checkNRVO() | |
2806 | { | |
235d5a96 | 2807 | if (!isNRVO() || returns is null) |
5fee5ec3 IB |
2808 | return false; |
2809 | ||
2810 | auto tf = type.toTypeFunction(); | |
2811 | if (tf.isref) | |
2812 | return false; | |
2813 | ||
2814 | foreach (rs; *returns) | |
2815 | { | |
2816 | if (auto ve = rs.exp.isVarExp()) | |
2817 | { | |
2818 | auto v = ve.var.isVarDeclaration(); | |
235d5a96 | 2819 | if (!v || v.isReference()) |
5fee5ec3 IB |
2820 | return false; |
2821 | else if (nrvo_var is null) | |
2822 | { | |
2823 | // Variables in the data segment (e.g. globals, TLS or not), | |
2824 | // parameters and closure variables cannot be NRVOed. | |
2825 | if (v.isDataseg() || v.isParameter() || v.toParent2() != this) | |
2826 | return false; | |
235d5a96 IB |
2827 | if (v.nestedrefs.length && needsClosure()) |
2828 | return false; | |
5fee5ec3 IB |
2829 | // The variable type needs to be equivalent to the return type. |
2830 | if (!v.type.equivalent(tf.next)) | |
2831 | return false; | |
2832 | //printf("Setting nrvo to %s\n", v.toChars()); | |
2833 | nrvo_var = v; | |
2834 | } | |
2835 | else if (nrvo_var != v) | |
2836 | return false; | |
2837 | } | |
2838 | else //if (!exp.isLvalue()) // keep NRVO-ability | |
2839 | return false; | |
2840 | } | |
2841 | return true; | |
2842 | } | |
2843 | ||
2844 | override final inout(FuncDeclaration) isFuncDeclaration() inout | |
2845 | { | |
2846 | return this; | |
2847 | } | |
2848 | ||
2849 | inout(FuncDeclaration) toAliasFunc() inout | |
2850 | { | |
2851 | return this; | |
2852 | } | |
2853 | ||
2854 | override void accept(Visitor v) | |
2855 | { | |
2856 | v.visit(this); | |
2857 | } | |
2858 | } | |
2859 | ||
2860 | /******************************************************** | |
2861 | * Generate Expression to call the invariant. | |
2862 | * Input: | |
2863 | * ad aggregate with the invariant | |
2864 | * vthis variable with 'this' | |
2865 | * Returns: | |
2866 | * void expression that calls the invariant | |
2867 | */ | |
2868 | Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) | |
2869 | { | |
2870 | Expression e = null; | |
2871 | // Call invariant directly only if it exists | |
2872 | FuncDeclaration inv = ad.inv; | |
2873 | ClassDeclaration cd = ad.isClassDeclaration(); | |
2874 | ||
2875 | while (!inv && cd) | |
2876 | { | |
2877 | cd = cd.baseClass; | |
2878 | if (!cd) | |
2879 | break; | |
2880 | inv = cd.inv; | |
2881 | } | |
2882 | if (inv) | |
2883 | { | |
2884 | version (all) | |
2885 | { | |
2886 | // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 | |
2887 | // For the correct mangling, | |
2888 | // run attribute inference on inv if needed. | |
2889 | inv.functionSemantic(); | |
2890 | } | |
2891 | ||
2892 | //e = new DsymbolExp(Loc.initial, inv); | |
2893 | //e = new CallExp(Loc.initial, e); | |
2894 | //e = e.semantic(sc2); | |
2895 | ||
2896 | /* https://issues.dlang.org/show_bug.cgi?id=13113 | |
2897 | * Currently virtual invariant calls completely | |
2898 | * bypass attribute enforcement. | |
2899 | * Change the behavior of pre-invariant call by following it. | |
2900 | */ | |
2901 | e = new ThisExp(Loc.initial); | |
2902 | e.type = ad.type.addMod(vthis.type.mod); | |
2903 | e = new DotVarExp(Loc.initial, e, inv, false); | |
2904 | e.type = inv.type; | |
2905 | e = new CallExp(Loc.initial, e); | |
2906 | e.type = Type.tvoid; | |
2907 | } | |
2908 | return e; | |
2909 | } | |
2910 | ||
2911 | /*************************************************** | |
2912 | * Visit each overloaded function/template in turn, and call dg(s) on it. | |
2913 | * Exit when no more, or dg(s) returns nonzero. | |
2914 | * | |
2915 | * Params: | |
2916 | * fstart = symbol to start from | |
2917 | * dg = the delegate to be called on the overload | |
2918 | * sc = context used to check if symbol is accessible (and therefore visible), | |
2919 | * can be null | |
2920 | * | |
2921 | * Returns: | |
2922 | * ==0 continue | |
2923 | * !=0 done (and the return value from the last dg() call) | |
2924 | */ | |
2925 | extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) | |
2926 | { | |
2927 | Dsymbol next; | |
2928 | for (auto d = fstart; d; d = next) | |
2929 | { | |
2930 | import dmd.access : checkSymbolAccess; | |
2931 | if (auto od = d.isOverDeclaration()) | |
2932 | { | |
2933 | /* The scope is needed here to check whether a function in | |
2934 | an overload set was added by means of a private alias (or a | |
2935 | selective import). If the scope where the alias is created | |
2936 | is imported somewhere, the overload set is visible, but the private | |
2937 | alias is not. | |
2938 | */ | |
2939 | if (sc) | |
2940 | { | |
2941 | if (checkSymbolAccess(sc, od)) | |
2942 | { | |
2943 | if (int r = overloadApply(od.aliassym, dg, sc)) | |
2944 | return r; | |
2945 | } | |
2946 | } | |
2947 | else if (int r = overloadApply(od.aliassym, dg, sc)) | |
2948 | return r; | |
2949 | next = od.overnext; | |
2950 | } | |
2951 | else if (auto fa = d.isFuncAliasDeclaration()) | |
2952 | { | |
2953 | if (fa.hasOverloads) | |
2954 | { | |
2955 | if (int r = overloadApply(fa.funcalias, dg, sc)) | |
2956 | return r; | |
2957 | } | |
2958 | else if (auto fd = fa.toAliasFunc()) | |
2959 | { | |
2960 | if (int r = dg(fd)) | |
2961 | return r; | |
2962 | } | |
2963 | else | |
2964 | { | |
2965 | d.error("is aliased to a function"); | |
2966 | break; | |
2967 | } | |
2968 | next = fa.overnext; | |
2969 | } | |
2970 | else if (auto ad = d.isAliasDeclaration()) | |
2971 | { | |
2972 | if (sc) | |
2973 | { | |
2974 | if (checkSymbolAccess(sc, ad)) | |
2975 | next = ad.toAlias(); | |
2976 | } | |
2977 | else | |
2978 | next = ad.toAlias(); | |
2979 | if (next == ad) | |
2980 | break; | |
2981 | if (next == fstart) | |
2982 | break; | |
2983 | } | |
2984 | else if (auto td = d.isTemplateDeclaration()) | |
2985 | { | |
2986 | if (int r = dg(td)) | |
2987 | return r; | |
2988 | next = td.overnext; | |
2989 | } | |
2990 | else if (auto fd = d.isFuncDeclaration()) | |
2991 | { | |
2992 | if (int r = dg(fd)) | |
2993 | return r; | |
2994 | next = fd.overnext; | |
2995 | } | |
2996 | else if (auto os = d.isOverloadSet()) | |
2997 | { | |
2998 | foreach (ds; os.a) | |
2999 | if (int r = dg(ds)) | |
3000 | return r; | |
3001 | } | |
3002 | else | |
3003 | { | |
3004 | d.error("is aliased to a function"); | |
3005 | break; | |
3006 | // BUG: should print error message? | |
3007 | } | |
3008 | } | |
3009 | return 0; | |
3010 | } | |
3011 | ||
3012 | /** | |
3013 | Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the | |
3014 | mismatching modifiers to `buf`. | |
3015 | ||
3016 | The modifiers of the `lhsMod` mismatching the ones with the `rhsMod` are printed, i.e. | |
3017 | lhs(shared) vs. rhs() prints "`shared`", wheras lhs() vs rhs(shared) prints "non-shared". | |
3018 | ||
3019 | Params: | |
3020 | buf = output buffer to write to | |
3021 | lhsMod = modifier on the left-hand side | |
3022 | lhsMod = modifier on the right-hand side | |
3023 | ||
3024 | Returns: | |
3025 | ||
3026 | A tuple with `isMutable` and `isNotShared` set | |
3027 | if the `lhsMod` is missing those modifiers (compared to rhs). | |
3028 | */ | |
3029 | auto MODMatchToBuffer(OutBuffer* buf, ubyte lhsMod, ubyte rhsMod) | |
3030 | { | |
3031 | static struct Mismatches | |
3032 | { | |
3033 | bool isNotShared; | |
3034 | bool isMutable; | |
3035 | } | |
3036 | ||
3037 | Mismatches mismatches; | |
3038 | ||
3039 | bool bothMutable = ((lhsMod & rhsMod) == 0); | |
3040 | bool sharedMismatch = ((lhsMod ^ rhsMod) & MODFlags.shared_) != 0; | |
3041 | bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODFlags.shared_); | |
3042 | ||
3043 | if (lhsMod & MODFlags.shared_) | |
3044 | buf.writestring("`shared` "); | |
3045 | else if (sharedMismatch && !(lhsMod & MODFlags.immutable_)) | |
3046 | { | |
3047 | buf.writestring("non-shared "); | |
3048 | mismatches.isNotShared = true; | |
3049 | } | |
3050 | ||
3051 | if (bothMutable && sharedMismatchOnly) | |
3052 | { | |
3053 | } | |
3054 | else if (lhsMod & MODFlags.immutable_) | |
3055 | buf.writestring("`immutable` "); | |
3056 | else if (lhsMod & MODFlags.const_) | |
3057 | buf.writestring("`const` "); | |
3058 | else if (lhsMod & MODFlags.wild) | |
3059 | buf.writestring("`inout` "); | |
3060 | else | |
3061 | { | |
3062 | buf.writestring("mutable "); | |
3063 | mismatches.isMutable = true; | |
3064 | } | |
3065 | ||
3066 | return mismatches; | |
3067 | } | |
3068 | ||
3069 | /// | |
3070 | unittest | |
3071 | { | |
3072 | OutBuffer buf; | |
3073 | auto mismatches = MODMatchToBuffer(&buf, MODFlags.shared_, 0); | |
3074 | assert(buf[] == "`shared` "); | |
3075 | assert(!mismatches.isNotShared); | |
3076 | ||
3077 | buf.setsize(0); | |
3078 | mismatches = MODMatchToBuffer(&buf, 0, MODFlags.shared_); | |
3079 | assert(buf[] == "non-shared "); | |
3080 | assert(mismatches.isNotShared); | |
3081 | ||
3082 | buf.setsize(0); | |
3083 | mismatches = MODMatchToBuffer(&buf, MODFlags.const_, 0); | |
3084 | assert(buf[] == "`const` "); | |
3085 | assert(!mismatches.isMutable); | |
3086 | ||
3087 | buf.setsize(0); | |
3088 | mismatches = MODMatchToBuffer(&buf, 0, MODFlags.const_); | |
3089 | assert(buf[] == "mutable "); | |
3090 | assert(mismatches.isMutable); | |
3091 | } | |
3092 | ||
3093 | private const(char)* prependSpace(const(char)* str) | |
3094 | { | |
3095 | if (!str || !*str) return ""; | |
3096 | ||
3097 | return (" " ~ str.toDString() ~ "\0").ptr; | |
3098 | } | |
3099 | ||
3100 | /// Flag used by $(LREF resolveFuncCall). | |
3101 | enum FuncResolveFlag : ubyte | |
3102 | { | |
3103 | standard = 0, /// issue error messages, solve the call. | |
3104 | quiet = 1, /// do not issue error message on no match, just return `null`. | |
3105 | overloadOnly = 2, /// only resolve overloads, i.e. do not issue error on ambiguous | |
3106 | /// matches and need explicit this. | |
3107 | } | |
3108 | ||
3109 | /******************************************* | |
3110 | * Given a symbol that could be either a FuncDeclaration or | |
3111 | * a function template, resolve it to a function symbol. | |
3112 | * Params: | |
3113 | * loc = instantiation location | |
3114 | * sc = instantiation scope | |
3115 | * s = instantiation symbol | |
3116 | * tiargs = initial list of template arguments | |
3117 | * tthis = if !NULL, the `this` argument type | |
3118 | * fargs = arguments to function | |
3119 | * flags = see $(LREF FuncResolveFlag). | |
3120 | * Returns: | |
3121 | * if match is found, then function symbol, else null | |
3122 | */ | |
3123 | FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, | |
3124 | Objects* tiargs, Type tthis, Expressions* fargs, FuncResolveFlag flags) | |
3125 | { | |
3126 | if (!s) | |
3127 | return null; // no match | |
3128 | ||
3129 | version (none) | |
3130 | { | |
3131 | printf("resolveFuncCall('%s')\n", s.toChars()); | |
3132 | if (tthis) | |
3133 | printf("\tthis: %s\n", tthis.toChars()); | |
3134 | if (fargs) | |
3135 | { | |
3136 | for (size_t i = 0; i < fargs.dim; i++) | |
3137 | { | |
3138 | Expression arg = (*fargs)[i]; | |
3139 | assert(arg.type); | |
3140 | printf("\t%s: %s\n", arg.toChars(), arg.type.toChars()); | |
3141 | } | |
3142 | } | |
3143 | } | |
3144 | ||
c8dfa79c | 3145 | if (tiargs && arrayObjectIsError(tiargs)) |
5fee5ec3 | 3146 | return null; |
c8dfa79c IB |
3147 | if (fargs !is null) |
3148 | foreach (arg; *fargs) | |
3149 | if (isError(arg)) | |
3150 | return null; | |
5fee5ec3 IB |
3151 | |
3152 | MatchAccumulator m; | |
3153 | functionResolve(m, s, loc, sc, tiargs, tthis, fargs, null); | |
3154 | auto orig_s = s; | |
3155 | ||
3156 | if (m.last > MATCH.nomatch && m.lastf) | |
3157 | { | |
3158 | if (m.count == 1) // exactly one match | |
3159 | { | |
3160 | if (!(flags & FuncResolveFlag.quiet)) | |
3161 | m.lastf.functionSemantic(); | |
3162 | return m.lastf; | |
3163 | } | |
3164 | if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis()) | |
3165 | { | |
3166 | return m.lastf; | |
3167 | } | |
3168 | } | |
3169 | ||
3170 | /* Failed to find a best match. | |
3171 | * Do nothing or print error. | |
3172 | */ | |
3173 | if (m.last == MATCH.nomatch) | |
3174 | { | |
3175 | // error was caused on matched function, not on the matching itself, | |
3176 | // so return the function to produce a better diagnostic | |
3177 | if (m.count == 1) | |
3178 | return m.lastf; | |
3179 | } | |
3180 | ||
3181 | // We are done at this point, as the rest of this function generate | |
3182 | // a diagnostic on invalid match | |
3183 | if (flags & FuncResolveFlag.quiet) | |
3184 | return null; | |
3185 | ||
3186 | auto fd = s.isFuncDeclaration(); | |
3187 | auto od = s.isOverDeclaration(); | |
3188 | auto td = s.isTemplateDeclaration(); | |
3189 | if (td && td.funcroot) | |
3190 | s = fd = td.funcroot; | |
3191 | ||
3192 | OutBuffer tiargsBuf; | |
3193 | arrayObjectsToBuffer(&tiargsBuf, tiargs); | |
3194 | ||
3195 | OutBuffer fargsBuf; | |
3196 | fargsBuf.writeByte('('); | |
3197 | argExpTypesToCBuffer(&fargsBuf, fargs); | |
3198 | fargsBuf.writeByte(')'); | |
3199 | if (tthis) | |
3200 | tthis.modToBuffer(&fargsBuf); | |
3201 | ||
3202 | // The call is ambiguous | |
3203 | if (m.lastf && m.nextf) | |
3204 | { | |
3205 | TypeFunction tf1 = m.lastf.type.toTypeFunction(); | |
3206 | TypeFunction tf2 = m.nextf.type.toTypeFunction(); | |
3207 | const(char)* lastprms = parametersTypeToChars(tf1.parameterList); | |
3208 | const(char)* nextprms = parametersTypeToChars(tf2.parameterList); | |
3209 | ||
3210 | const(char)* mod1 = prependSpace(MODtoChars(tf1.mod)); | |
3211 | const(char)* mod2 = prependSpace(MODtoChars(tf2.mod)); | |
3212 | ||
3213 | .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", | |
3214 | s.parent.toPrettyChars(), s.ident.toChars(), | |
3215 | fargsBuf.peekChars(), | |
3216 | m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, mod1, | |
3217 | m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, mod2); | |
3218 | return null; | |
3219 | } | |
3220 | ||
3221 | // no match, generate an error messages | |
3222 | if (!fd) | |
3223 | { | |
3224 | // all of overloads are templates | |
3225 | if (td) | |
3226 | { | |
d7569187 | 3227 | .error(loc, "none of the overloads of %s `%s.%s` are callable using argument types `!(%s)%s`", |
5fee5ec3 IB |
3228 | td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), |
3229 | tiargsBuf.peekChars(), fargsBuf.peekChars()); | |
3230 | ||
b6df1132 IB |
3231 | if (!global.gag || global.params.showGaggedErrors) |
3232 | printCandidates(loc, td, sc.isDeprecated()); | |
5fee5ec3 IB |
3233 | return null; |
3234 | } | |
3235 | /* This case used to happen when several ctors are mixed in an agregate. | |
3236 | A (bad) error message is already generated in overloadApply(). | |
3237 | see https://issues.dlang.org/show_bug.cgi?id=19729 | |
3238 | and https://issues.dlang.org/show_bug.cgi?id=17259 | |
3239 | */ | |
3240 | if (!od) | |
3241 | return null; | |
3242 | } | |
3243 | ||
3244 | if (od) | |
3245 | { | |
3246 | .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", | |
3247 | od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); | |
3248 | return null; | |
3249 | } | |
3250 | ||
3251 | // remove when deprecation period of class allocators and deallocators is over | |
3252 | if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) | |
3253 | return null; | |
3254 | ||
3255 | bool hasOverloads = fd.overnext !is null; | |
0fb57034 IB |
3256 | auto tf = fd.type.isTypeFunction(); |
3257 | // if type is an error, the original type should be there for better diagnostics | |
3258 | if (!tf) | |
3259 | tf = fd.originalType.toTypeFunction(); | |
3260 | ||
5fee5ec3 IB |
3261 | if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch |
3262 | { | |
3263 | OutBuffer thisBuf, funcBuf; | |
3264 | MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); | |
3265 | auto mismatches = MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); | |
3266 | if (hasOverloads) | |
3267 | { | |
3268 | .error(loc, "none of the overloads of `%s` are callable using a %sobject", | |
3269 | fd.ident.toChars(), thisBuf.peekChars()); | |
b6df1132 IB |
3270 | if (!global.gag || global.params.showGaggedErrors) |
3271 | printCandidates(loc, fd, sc.isDeprecated()); | |
5fee5ec3 IB |
3272 | return null; |
3273 | } | |
3274 | ||
3275 | const(char)* failMessage; | |
3276 | functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); | |
3277 | if (failMessage) | |
3278 | { | |
3279 | .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", | |
3280 | fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), | |
3281 | tf.modToChars(), fargsBuf.peekChars()); | |
3282 | errorSupplemental(loc, failMessage); | |
3283 | return null; | |
3284 | } | |
3285 | ||
3286 | .error(loc, "%smethod `%s` is not callable using a %sobject", | |
3287 | funcBuf.peekChars(), fd.toPrettyChars(), thisBuf.peekChars()); | |
3288 | ||
3289 | if (mismatches.isNotShared) | |
3290 | .errorSupplemental(fd.loc, "Consider adding `shared` here"); | |
3291 | else if (mismatches.isMutable) | |
3292 | .errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); | |
3293 | return null; | |
3294 | } | |
3295 | ||
3296 | //printf("tf = %s, args = %s\n", tf.deco, (*fargs)[0].type.deco); | |
3297 | if (hasOverloads) | |
3298 | { | |
3299 | .error(loc, "none of the overloads of `%s` are callable using argument types `%s`", | |
3300 | fd.toChars(), fargsBuf.peekChars()); | |
b6df1132 IB |
3301 | if (!global.gag || global.params.showGaggedErrors) |
3302 | printCandidates(loc, fd, sc.isDeprecated()); | |
5fee5ec3 IB |
3303 | return null; |
3304 | } | |
3305 | ||
3306 | .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", | |
3307 | fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), | |
3308 | tf.modToChars(), fargsBuf.peekChars()); | |
b6df1132 | 3309 | |
5fee5ec3 | 3310 | // re-resolve to check for supplemental message |
b6df1132 IB |
3311 | if (!global.gag || global.params.showGaggedErrors) |
3312 | { | |
7e7ebe3e IB |
3313 | if (tthis) |
3314 | { | |
3315 | if (auto classType = tthis.isTypeClass()) | |
3316 | { | |
3317 | if (auto baseClass = classType.sym.baseClass) | |
3318 | { | |
3319 | if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) | |
3320 | { | |
3321 | MatchAccumulator mErr; | |
3322 | functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, fargs, null); | |
3323 | if (mErr.last > MATCH.nomatch && mErr.lastf) | |
3324 | { | |
3325 | errorSupplemental(loc, "%s `%s` hides base class function `%s`", | |
3326 | fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); | |
3327 | errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", | |
3328 | fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); | |
3329 | return null; | |
3330 | } | |
3331 | } | |
3332 | } | |
3333 | } | |
3334 | } | |
b6df1132 IB |
3335 | const(char)* failMessage; |
3336 | functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); | |
3337 | if (failMessage) | |
3338 | errorSupplemental(loc, failMessage); | |
3339 | } | |
5fee5ec3 IB |
3340 | return null; |
3341 | } | |
3342 | ||
3343 | /******************************************* | |
3344 | * Prints template and function overload candidates as supplemental errors. | |
3345 | * Params: | |
3346 | * loc = instantiation location | |
3347 | * declaration = the declaration to print overload candidates for | |
3348 | * showDeprecated = If `false`, `deprecated` function won't be shown | |
3349 | */ | |
3350 | private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) | |
3351 | if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) | |
3352 | { | |
3353 | // max num of overloads to print (-v overrides this). | |
3354 | enum int DisplayLimit = 5; | |
3355 | int displayed; | |
3356 | const(char)* constraintsTip; | |
3357 | ||
3358 | // determine if the first candidate was printed | |
3359 | bool printed = false; | |
3360 | ||
3361 | overloadApply(declaration, (Dsymbol s) | |
3362 | { | |
3363 | Dsymbol nextOverload; | |
3364 | ||
3365 | if (auto fd = s.isFuncDeclaration()) | |
3366 | { | |
3367 | // Don't print overloads which have errors. | |
3368 | // Not that if the whole overload set has errors, we'll never reach | |
3369 | // this point so there's no risk of printing no candidate | |
3370 | if (fd.errors || fd.type.ty == Terror) | |
3371 | return 0; | |
3372 | // Don't print disabled functions, or `deprecated` outside of deprecated scope | |
3373 | if (fd.storage_class & STC.disable || (fd.isDeprecated() && !showDeprecated)) | |
3374 | return 0; | |
3375 | ||
3376 | const single_candidate = fd.overnext is null; | |
3377 | auto tf = cast(TypeFunction) fd.type; | |
3378 | .errorSupplemental(fd.loc, | |
3379 | printed ? " `%s%s`" : | |
3380 | single_candidate ? "Candidate is: `%s%s`" : "Candidates are: `%s%s`", | |
3381 | fd.toPrettyChars(), | |
3382 | parametersTypeToChars(tf.parameterList)); | |
3383 | printed = true; | |
3384 | nextOverload = fd.overnext; | |
3385 | } | |
3386 | else if (auto td = s.isTemplateDeclaration()) | |
3387 | { | |
3388 | import dmd.staticcond; | |
3389 | ||
3390 | const tmsg = td.toCharsNoConstraints(); | |
3391 | const cmsg = td.getConstraintEvalError(constraintsTip); | |
3392 | ||
3393 | const single_candidate = td.overnext is null; | |
3394 | ||
3395 | // add blank space if there are multiple candidates | |
3396 | // the length of the blank space is `strlen("Candidates are: ")` | |
3397 | ||
3398 | if (cmsg) | |
3399 | { | |
3400 | .errorSupplemental(td.loc, | |
3401 | printed ? " `%s`\n%s" : | |
3402 | single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s", | |
3403 | tmsg, cmsg); | |
3404 | printed = true; | |
3405 | } | |
3406 | else | |
3407 | { | |
3408 | .errorSupplemental(td.loc, | |
3409 | printed ? " `%s`" : | |
3410 | single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", | |
3411 | tmsg); | |
3412 | printed = true; | |
3413 | } | |
3414 | nextOverload = td.overnext; | |
3415 | } | |
3416 | ||
3417 | if (global.params.verbose || ++displayed < DisplayLimit) | |
3418 | return 0; | |
3419 | ||
3420 | // Too many overloads to sensibly display. | |
3421 | // Just show count of remaining overloads. | |
3422 | int num = 0; | |
3423 | overloadApply(nextOverload, (s) { ++num; return 0; }); | |
3424 | ||
3425 | if (num > 0) | |
3426 | .errorSupplemental(loc, "... (%d more, -v to show) ...", num); | |
3427 | return 1; // stop iterating | |
3428 | }); | |
3429 | ||
3430 | // Nothing was displayed, all overloads are either disabled or deprecated | |
3431 | if (!displayed) | |
3432 | .errorSupplemental(loc, "All possible candidates are marked as `deprecated` or `@disable`"); | |
3433 | // should be only in verbose mode | |
3434 | if (constraintsTip) | |
3435 | .tip(constraintsTip); | |
3436 | } | |
3437 | ||
3438 | /************************************** | |
3439 | * Returns an indirect type one step from t. | |
3440 | */ | |
3441 | Type getIndirection(Type t) | |
3442 | { | |
3443 | t = t.baseElemOf(); | |
3444 | if (t.ty == Tarray || t.ty == Tpointer) | |
3445 | return t.nextOf().toBasetype(); | |
3446 | if (t.ty == Taarray || t.ty == Tclass) | |
3447 | return t; | |
3448 | if (t.ty == Tstruct) | |
3449 | return t.hasPointers() ? t : null; // TODO | |
3450 | ||
3451 | // should consider TypeDelegate? | |
3452 | return null; | |
3453 | } | |
3454 | ||
3455 | /************************************** | |
3456 | * Performs type-based alias analysis between a newly created value and a pre- | |
3457 | * existing memory reference: | |
3458 | * | |
3459 | * Assuming that a reference A to a value of type `ta` was available to the code | |
3460 | * that created a reference B to a value of type `tb`, it returns whether B | |
3461 | * might alias memory reachable from A based on the types involved (either | |
3462 | * directly or via any number of indirections in either A or B). | |
3463 | * | |
3464 | * This relation is not symmetric in the two arguments. For example, a | |
3465 | * a `const(int)` reference can point to a pre-existing `int`, but not the other | |
3466 | * way round. | |
3467 | * | |
3468 | * Examples: | |
3469 | * | |
3470 | * ta, tb, result | |
3471 | * `const(int)`, `int`, `false` | |
3472 | * `int`, `const(int)`, `true` | |
3473 | * `int`, `immutable(int)`, `false` | |
3474 | * const(immutable(int)*), immutable(int)*, false // BUG: returns true | |
3475 | * | |
3476 | * Params: | |
3477 | * ta = value type being referred to | |
3478 | * tb = referred to value type that could be constructed from ta | |
3479 | * | |
3480 | * Returns: | |
3481 | * true if reference to `tb` is isolated from reference to `ta` | |
3482 | */ | |
3483 | private bool traverseIndirections(Type ta, Type tb) | |
3484 | { | |
3485 | //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars()); | |
3486 | ||
0fb57034 | 3487 | static bool traverse(Type ta, Type tb, ref scope AssocArray!(const(char)*, bool) table, bool reversePass) |
5fee5ec3 IB |
3488 | { |
3489 | //printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars()); | |
3490 | ta = ta.baseElemOf(); | |
3491 | tb = tb.baseElemOf(); | |
3492 | ||
3493 | // First, check if the pointed-to types are convertible to each other such | |
3494 | // that they might alias directly. | |
3495 | static bool mayAliasDirect(Type source, Type target) | |
3496 | { | |
3497 | return | |
3498 | // if source is the same as target or can be const-converted to target | |
3499 | source.constConv(target) != MATCH.nomatch || | |
3500 | // if target is void and source can be const-converted to target | |
3501 | (target.ty == Tvoid && MODimplicitConv(source.mod, target.mod)); | |
3502 | } | |
3503 | ||
3504 | if (mayAliasDirect(reversePass ? tb : ta, reversePass ? ta : tb)) | |
3505 | { | |
3506 | //printf(" true mayalias %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); | |
3507 | return false; | |
3508 | } | |
3509 | if (ta.nextOf() && ta.nextOf() == tb.nextOf()) | |
3510 | { | |
3511 | //printf(" next==next %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); | |
3512 | return true; | |
3513 | } | |
3514 | ||
3515 | if (tb.ty == Tclass || tb.ty == Tstruct) | |
3516 | { | |
5fee5ec3 IB |
3517 | /* Traverse the type of each field of the aggregate |
3518 | */ | |
0fb57034 IB |
3519 | bool* found = table.getLvalue(tb.deco); |
3520 | if (*found == true) | |
3521 | return true; // We have already seen this symbol, break the cycle | |
3522 | else | |
3523 | *found = true; | |
3524 | ||
5fee5ec3 IB |
3525 | AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration(); |
3526 | foreach (v; sym.fields) | |
3527 | { | |
3528 | Type tprmi = v.type.addMod(tb.mod); | |
3529 | //printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars()); | |
0fb57034 | 3530 | if (!traverse(ta, tprmi, table, reversePass)) |
5fee5ec3 IB |
3531 | return false; |
3532 | } | |
3533 | } | |
3534 | else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer) | |
3535 | { | |
3536 | Type tind = tb.nextOf(); | |
0fb57034 | 3537 | if (!traverse(ta, tind, table, reversePass)) |
5fee5ec3 IB |
3538 | return false; |
3539 | } | |
3540 | else if (tb.hasPointers()) | |
3541 | { | |
3542 | // BUG: consider the context pointer of delegate types | |
3543 | return false; | |
3544 | } | |
3545 | ||
3546 | // Still no match, so try breaking up ta if we have not done so yet. | |
3547 | if (!reversePass) | |
0fb57034 IB |
3548 | { |
3549 | scope newTable = AssocArray!(const(char)*, bool)(); | |
3550 | return traverse(tb, ta, newTable, true); | |
3551 | } | |
5fee5ec3 IB |
3552 | |
3553 | return true; | |
3554 | } | |
3555 | ||
3556 | // To handle arbitrary levels of indirections in both parameters, we | |
3557 | // recursively descend into aggregate members/levels of indirection in both | |
3558 | // `ta` and `tb` while avoiding cycles. Start with the original types. | |
0fb57034 IB |
3559 | scope table = AssocArray!(const(char)*, bool)(); |
3560 | const result = traverse(ta, tb, table, false); | |
5fee5ec3 IB |
3561 | //printf(" returns %d\n", result); |
3562 | return result; | |
3563 | } | |
3564 | ||
3565 | /* For all functions between outerFunc and f, mark them as needing | |
3566 | * a closure. | |
3567 | */ | |
3568 | private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc) | |
3569 | { | |
3570 | for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.toParentP(outerFunc)) | |
3571 | { | |
3572 | FuncDeclaration fy = sx.isFuncDeclaration(); | |
3573 | if (fy && fy.closureVars.dim) | |
3574 | { | |
3575 | /* fy needs a closure if it has closureVars[], | |
3576 | * because the frame pointer in the closure will be accessed. | |
3577 | */ | |
3578 | fy.requiresClosure = true; | |
3579 | } | |
3580 | } | |
3581 | } | |
3582 | ||
3583 | /******** | |
3584 | * Given a nested function f inside a function outerFunc, check | |
3585 | * if any sibling callers of f have escaped. If so, mark | |
3586 | * all the enclosing functions as needing closures. | |
3587 | * This is recursive: we need to check the callers of our siblings. | |
3588 | * Note that nested functions can only call lexically earlier nested | |
3589 | * functions, so loops are impossible. | |
3590 | * Params: | |
3591 | * f = inner function (nested within outerFunc) | |
3592 | * outerFunc = outer function | |
3593 | * p = for internal recursion use | |
3594 | * Returns: | |
3595 | * true if any closures were needed | |
3596 | */ | |
3597 | private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) | |
3598 | { | |
3599 | static struct PrevSibling | |
3600 | { | |
3601 | PrevSibling* p; | |
3602 | FuncDeclaration f; | |
3603 | } | |
3604 | ||
3605 | PrevSibling ps; | |
3606 | ps.p = cast(PrevSibling*)p; | |
3607 | ps.f = f; | |
3608 | ||
3609 | //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f.toChars(), outerFunc.toChars()); | |
3610 | bool bAnyClosures = false; | |
3611 | for (size_t i = 0; i < f.siblingCallers.dim; ++i) | |
3612 | { | |
3613 | FuncDeclaration g = f.siblingCallers[i]; | |
3614 | if (g.isThis() || g.tookAddressOf) | |
3615 | { | |
3616 | markAsNeedingClosure(g, outerFunc); | |
3617 | bAnyClosures = true; | |
3618 | } | |
3619 | ||
3620 | for (auto parent = g.toParentP(outerFunc); parent && parent !is outerFunc; parent = parent.toParentP(outerFunc)) | |
3621 | { | |
3622 | // A parent of the sibling had its address taken. | |
3623 | // Assume escaping of parent affects its children, so needs propagating. | |
3624 | // see https://issues.dlang.org/show_bug.cgi?id=19679 | |
3625 | FuncDeclaration parentFunc = parent.isFuncDeclaration; | |
3626 | if (parentFunc && parentFunc.tookAddressOf) | |
3627 | { | |
3628 | markAsNeedingClosure(parentFunc, outerFunc); | |
3629 | bAnyClosures = true; | |
3630 | } | |
3631 | } | |
3632 | ||
3633 | PrevSibling* prev = cast(PrevSibling*)p; | |
3634 | while (1) | |
3635 | { | |
3636 | if (!prev) | |
3637 | { | |
3638 | bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps); | |
3639 | break; | |
3640 | } | |
3641 | if (prev.f == g) | |
3642 | break; | |
3643 | prev = prev.p; | |
3644 | } | |
3645 | } | |
3646 | //printf("\t%d\n", bAnyClosures); | |
3647 | return bAnyClosures; | |
3648 | } | |
3649 | ||
3650 | /*********************************************************** | |
3651 | * Used as a way to import a set of functions from another scope into this one. | |
3652 | */ | |
3653 | extern (C++) final class FuncAliasDeclaration : FuncDeclaration | |
3654 | { | |
3655 | FuncDeclaration funcalias; | |
3656 | bool hasOverloads; | |
3657 | ||
3658 | extern (D) this(Identifier ident, FuncDeclaration funcalias, bool hasOverloads = true) | |
3659 | { | |
3660 | super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type); | |
3661 | assert(funcalias != this); | |
3662 | this.funcalias = funcalias; | |
3663 | ||
3664 | this.hasOverloads = hasOverloads; | |
3665 | if (hasOverloads) | |
3666 | { | |
3667 | if (FuncAliasDeclaration fad = funcalias.isFuncAliasDeclaration()) | |
3668 | this.hasOverloads = fad.hasOverloads; | |
3669 | } | |
3670 | else | |
3671 | { | |
3672 | // for internal use | |
3673 | assert(!funcalias.isFuncAliasDeclaration()); | |
3674 | this.hasOverloads = false; | |
3675 | } | |
3676 | userAttribDecl = funcalias.userAttribDecl; | |
3677 | } | |
3678 | ||
3679 | override inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout | |
3680 | { | |
3681 | return this; | |
3682 | } | |
3683 | ||
3684 | override const(char)* kind() const | |
3685 | { | |
3686 | return "function alias"; | |
3687 | } | |
3688 | ||
3689 | override inout(FuncDeclaration) toAliasFunc() inout | |
3690 | { | |
3691 | return funcalias.toAliasFunc(); | |
3692 | } | |
3693 | ||
3694 | override void accept(Visitor v) | |
3695 | { | |
3696 | v.visit(this); | |
3697 | } | |
3698 | } | |
3699 | ||
3700 | /*********************************************************** | |
3701 | */ | |
3702 | extern (C++) final class FuncLiteralDeclaration : FuncDeclaration | |
3703 | { | |
3704 | TOK tok; // TOK.function_ or TOK.delegate_ | |
3705 | Type treq; // target of return type inference | |
3706 | ||
3707 | // backend | |
3708 | bool deferToObj; | |
3709 | ||
c8dfa79c | 3710 | extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, StorageClass storage_class = STC.undefined_) |
5fee5ec3 | 3711 | { |
c8dfa79c | 3712 | super(loc, endloc, null, storage_class, type); |
5fee5ec3 IB |
3713 | this.ident = id ? id : Id.empty; |
3714 | this.tok = tok; | |
3715 | this.fes = fes; | |
3716 | // Always infer scope for function literals | |
3717 | // See https://issues.dlang.org/show_bug.cgi?id=20362 | |
7e7ebe3e | 3718 | this.inferScope = true; |
5fee5ec3 IB |
3719 | //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars()); |
3720 | } | |
3721 | ||
3722 | override FuncLiteralDeclaration syntaxCopy(Dsymbol s) | |
3723 | { | |
3724 | //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars()); | |
3725 | assert(!s); | |
c8dfa79c | 3726 | auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident, storage_class & STC.auto_); |
5fee5ec3 IB |
3727 | f.treq = treq; // don't need to copy |
3728 | FuncDeclaration.syntaxCopy(f); | |
3729 | return f; | |
3730 | } | |
3731 | ||
3732 | override bool isNested() const | |
3733 | { | |
3734 | //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars()); | |
3735 | return (tok != TOK.function_) && !isThis(); | |
3736 | } | |
3737 | ||
3738 | override inout(AggregateDeclaration) isThis() inout | |
3739 | { | |
3740 | return tok == TOK.delegate_ ? super.isThis() : null; | |
3741 | } | |
3742 | ||
3743 | override bool isVirtual() const | |
3744 | { | |
3745 | return false; | |
3746 | } | |
3747 | ||
3748 | override bool addPreInvariant() | |
3749 | { | |
3750 | return false; | |
3751 | } | |
3752 | ||
3753 | override bool addPostInvariant() | |
3754 | { | |
3755 | return false; | |
3756 | } | |
3757 | ||
3758 | /******************************* | |
3759 | * Modify all expression type of return statements to tret. | |
3760 | * | |
3761 | * On function literals, return type may be modified based on the context type | |
3762 | * after its semantic3 is done, in FuncExp::implicitCastTo. | |
3763 | * | |
3764 | * A function() dg = (){ return new B(); } // OK if is(B : A) == true | |
3765 | * | |
3766 | * If B to A conversion is convariant that requires offseet adjusting, | |
3767 | * all return statements should be adjusted to return expressions typed A. | |
3768 | */ | |
3769 | void modifyReturns(Scope* sc, Type tret) | |
3770 | { | |
3771 | import dmd.statement_rewrite_walker; | |
3772 | ||
3773 | extern (C++) final class RetWalker : StatementRewriteWalker | |
3774 | { | |
3775 | alias visit = typeof(super).visit; | |
3776 | public: | |
3777 | Scope* sc; | |
3778 | Type tret; | |
3779 | FuncLiteralDeclaration fld; | |
3780 | ||
3781 | override void visit(ReturnStatement s) | |
3782 | { | |
3783 | Expression exp = s.exp; | |
3784 | if (exp && !exp.type.equals(tret)) | |
c8dfa79c | 3785 | s.exp = exp.implicitCastTo(sc, tret); |
5fee5ec3 IB |
3786 | } |
3787 | } | |
3788 | ||
3789 | if (semanticRun < PASS.semantic3done) | |
3790 | return; | |
3791 | ||
3792 | if (fes) | |
3793 | return; | |
3794 | ||
3795 | scope RetWalker w = new RetWalker(); | |
3796 | w.sc = sc; | |
3797 | w.tret = tret; | |
3798 | w.fld = this; | |
3799 | fbody.accept(w); | |
3800 | ||
3801 | // Also update the inferred function type to match the new return type. | |
3802 | // This is required so the code generator does not try to cast the | |
3803 | // modified returns back to the original type. | |
3804 | if (inferRetType && type.nextOf() != tret) | |
3805 | type.toTypeFunction().next = tret; | |
3806 | } | |
3807 | ||
3808 | override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout | |
3809 | { | |
3810 | return this; | |
3811 | } | |
3812 | ||
3813 | override const(char)* kind() const | |
3814 | { | |
3815 | // GCC requires the (char*) casts | |
3816 | return (tok != TOK.function_) ? "delegate" : "function"; | |
3817 | } | |
3818 | ||
3819 | override const(char)* toPrettyChars(bool QualifyTypes = false) | |
3820 | { | |
3821 | if (parent) | |
3822 | { | |
3823 | TemplateInstance ti = parent.isTemplateInstance(); | |
3824 | if (ti) | |
3825 | return ti.tempdecl.toPrettyChars(QualifyTypes); | |
3826 | } | |
3827 | return Dsymbol.toPrettyChars(QualifyTypes); | |
3828 | } | |
3829 | ||
3830 | override void accept(Visitor v) | |
3831 | { | |
3832 | v.visit(this); | |
3833 | } | |
3834 | } | |
3835 | ||
3836 | /*********************************************************** | |
3837 | */ | |
3838 | extern (C++) final class CtorDeclaration : FuncDeclaration | |
3839 | { | |
3840 | bool isCpCtor; | |
3841 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false) | |
3842 | { | |
3843 | super(loc, endloc, Id.ctor, stc, type); | |
3844 | this.isCpCtor = isCpCtor; | |
3845 | //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); | |
3846 | } | |
3847 | ||
3848 | override CtorDeclaration syntaxCopy(Dsymbol s) | |
3849 | { | |
3850 | assert(!s); | |
3851 | auto f = new CtorDeclaration(loc, endloc, storage_class, type.syntaxCopy()); | |
3852 | FuncDeclaration.syntaxCopy(f); | |
3853 | return f; | |
3854 | } | |
3855 | ||
3856 | override const(char)* kind() const | |
3857 | { | |
3858 | return isCpCtor ? "copy constructor" : "constructor"; | |
3859 | } | |
3860 | ||
3861 | override const(char)* toChars() const | |
3862 | { | |
3863 | return "this"; | |
3864 | } | |
3865 | ||
3866 | override bool isVirtual() const | |
3867 | { | |
3868 | return false; | |
3869 | } | |
3870 | ||
3871 | override bool addPreInvariant() | |
3872 | { | |
3873 | return false; | |
3874 | } | |
3875 | ||
3876 | override bool addPostInvariant() | |
3877 | { | |
3878 | return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); | |
3879 | } | |
3880 | ||
3881 | override inout(CtorDeclaration) isCtorDeclaration() inout | |
3882 | { | |
3883 | return this; | |
3884 | } | |
3885 | ||
3886 | override void accept(Visitor v) | |
3887 | { | |
3888 | v.visit(this); | |
3889 | } | |
3890 | } | |
3891 | ||
3892 | /*********************************************************** | |
3893 | */ | |
3894 | extern (C++) final class PostBlitDeclaration : FuncDeclaration | |
3895 | { | |
3896 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) | |
3897 | { | |
3898 | super(loc, endloc, id, stc, null); | |
3899 | } | |
3900 | ||
3901 | override PostBlitDeclaration syntaxCopy(Dsymbol s) | |
3902 | { | |
3903 | assert(!s); | |
3904 | auto dd = new PostBlitDeclaration(loc, endloc, storage_class, ident); | |
3905 | FuncDeclaration.syntaxCopy(dd); | |
3906 | return dd; | |
3907 | } | |
3908 | ||
3909 | override bool isVirtual() const | |
3910 | { | |
3911 | return false; | |
3912 | } | |
3913 | ||
3914 | override bool addPreInvariant() | |
3915 | { | |
3916 | return false; | |
3917 | } | |
3918 | ||
3919 | override bool addPostInvariant() | |
3920 | { | |
3921 | return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); | |
3922 | } | |
3923 | ||
3924 | override bool overloadInsert(Dsymbol s) | |
3925 | { | |
3926 | return false; // cannot overload postblits | |
3927 | } | |
3928 | ||
3929 | override inout(PostBlitDeclaration) isPostBlitDeclaration() inout | |
3930 | { | |
3931 | return this; | |
3932 | } | |
3933 | ||
3934 | override void accept(Visitor v) | |
3935 | { | |
3936 | v.visit(this); | |
3937 | } | |
3938 | } | |
3939 | ||
3940 | /*********************************************************** | |
3941 | */ | |
3942 | extern (C++) final class DtorDeclaration : FuncDeclaration | |
3943 | { | |
3944 | extern (D) this(const ref Loc loc, const ref Loc endloc) | |
3945 | { | |
3946 | super(loc, endloc, Id.dtor, STC.undefined_, null); | |
3947 | } | |
3948 | ||
3949 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) | |
3950 | { | |
3951 | super(loc, endloc, id, stc, null); | |
3952 | } | |
3953 | ||
3954 | override DtorDeclaration syntaxCopy(Dsymbol s) | |
3955 | { | |
3956 | assert(!s); | |
3957 | auto dd = new DtorDeclaration(loc, endloc, storage_class, ident); | |
3958 | FuncDeclaration.syntaxCopy(dd); | |
3959 | return dd; | |
3960 | } | |
3961 | ||
3962 | override const(char)* kind() const | |
3963 | { | |
3964 | return "destructor"; | |
3965 | } | |
3966 | ||
3967 | override const(char)* toChars() const | |
3968 | { | |
3969 | return "~this"; | |
3970 | } | |
3971 | ||
3972 | override bool isVirtual() const | |
3973 | { | |
3974 | // D dtor's don't get put into the vtbl[] | |
3975 | // this is a hack so that extern(C++) destructors report as virtual, which are manually added to the vtable | |
3976 | return vtblIndex != -1; | |
3977 | } | |
3978 | ||
3979 | override bool addPreInvariant() | |
3980 | { | |
3981 | return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); | |
3982 | } | |
3983 | ||
3984 | override bool addPostInvariant() | |
3985 | { | |
3986 | return false; | |
3987 | } | |
3988 | ||
3989 | override bool overloadInsert(Dsymbol s) | |
3990 | { | |
3991 | return false; // cannot overload destructors | |
3992 | } | |
3993 | ||
3994 | override inout(DtorDeclaration) isDtorDeclaration() inout | |
3995 | { | |
3996 | return this; | |
3997 | } | |
3998 | ||
3999 | override void accept(Visitor v) | |
4000 | { | |
4001 | v.visit(this); | |
4002 | } | |
4003 | } | |
4004 | ||
4005 | /*********************************************************** | |
4006 | */ | |
4007 | extern (C++) class StaticCtorDeclaration : FuncDeclaration | |
4008 | { | |
4009 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) | |
4010 | { | |
4011 | super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null); | |
4012 | } | |
4013 | ||
4014 | extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) | |
4015 | { | |
4016 | super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); | |
4017 | } | |
4018 | ||
4019 | override StaticCtorDeclaration syntaxCopy(Dsymbol s) | |
4020 | { | |
4021 | assert(!s); | |
4022 | auto scd = new StaticCtorDeclaration(loc, endloc, storage_class); | |
4023 | FuncDeclaration.syntaxCopy(scd); | |
4024 | return scd; | |
4025 | } | |
4026 | ||
4027 | override final inout(AggregateDeclaration) isThis() inout @nogc nothrow pure @safe | |
4028 | { | |
4029 | return null; | |
4030 | } | |
4031 | ||
4032 | override final bool isVirtual() const @nogc nothrow pure @safe | |
4033 | { | |
4034 | return false; | |
4035 | } | |
4036 | ||
4037 | override final bool addPreInvariant() @nogc nothrow pure @safe | |
4038 | { | |
4039 | return false; | |
4040 | } | |
4041 | ||
4042 | override final bool addPostInvariant() @nogc nothrow pure @safe | |
4043 | { | |
4044 | return false; | |
4045 | } | |
4046 | ||
4047 | override final bool hasStaticCtorOrDtor() @nogc nothrow pure @safe | |
4048 | { | |
4049 | return true; | |
4050 | } | |
4051 | ||
4052 | override final inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout @nogc nothrow pure @safe | |
4053 | { | |
4054 | return this; | |
4055 | } | |
4056 | ||
4057 | override void accept(Visitor v) | |
4058 | { | |
4059 | v.visit(this); | |
4060 | } | |
4061 | } | |
4062 | ||
4063 | /*********************************************************** | |
4064 | */ | |
4065 | extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration | |
4066 | { | |
4067 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) | |
4068 | { | |
4069 | super(loc, endloc, "_sharedStaticCtor", stc); | |
4070 | } | |
4071 | ||
4072 | override SharedStaticCtorDeclaration syntaxCopy(Dsymbol s) | |
4073 | { | |
4074 | assert(!s); | |
4075 | auto scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class); | |
4076 | FuncDeclaration.syntaxCopy(scd); | |
4077 | return scd; | |
4078 | } | |
4079 | ||
4080 | override inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout | |
4081 | { | |
4082 | return this; | |
4083 | } | |
4084 | ||
4085 | override void accept(Visitor v) | |
4086 | { | |
4087 | v.visit(this); | |
4088 | } | |
4089 | } | |
4090 | ||
4091 | /*********************************************************** | |
4092 | */ | |
4093 | extern (C++) class StaticDtorDeclaration : FuncDeclaration | |
4094 | { | |
4095 | VarDeclaration vgate; // 'gate' variable | |
4096 | ||
4097 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) | |
4098 | { | |
4099 | super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null); | |
4100 | } | |
4101 | ||
4102 | extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) | |
4103 | { | |
4104 | super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); | |
4105 | } | |
4106 | ||
4107 | override StaticDtorDeclaration syntaxCopy(Dsymbol s) | |
4108 | { | |
4109 | assert(!s); | |
4110 | auto sdd = new StaticDtorDeclaration(loc, endloc, storage_class); | |
4111 | FuncDeclaration.syntaxCopy(sdd); | |
4112 | return sdd; | |
4113 | } | |
4114 | ||
4115 | override final inout(AggregateDeclaration) isThis() inout | |
4116 | { | |
4117 | return null; | |
4118 | } | |
4119 | ||
4120 | override final bool isVirtual() const | |
4121 | { | |
4122 | return false; | |
4123 | } | |
4124 | ||
4125 | override final bool hasStaticCtorOrDtor() | |
4126 | { | |
4127 | return true; | |
4128 | } | |
4129 | ||
4130 | override final bool addPreInvariant() | |
4131 | { | |
4132 | return false; | |
4133 | } | |
4134 | ||
4135 | override final bool addPostInvariant() | |
4136 | { | |
4137 | return false; | |
4138 | } | |
4139 | ||
4140 | override final inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout | |
4141 | { | |
4142 | return this; | |
4143 | } | |
4144 | ||
4145 | override void accept(Visitor v) | |
4146 | { | |
4147 | v.visit(this); | |
4148 | } | |
4149 | } | |
4150 | ||
4151 | /*********************************************************** | |
4152 | */ | |
4153 | extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration | |
4154 | { | |
4155 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) | |
4156 | { | |
4157 | super(loc, endloc, "_sharedStaticDtor", stc); | |
4158 | } | |
4159 | ||
4160 | override SharedStaticDtorDeclaration syntaxCopy(Dsymbol s) | |
4161 | { | |
4162 | assert(!s); | |
4163 | auto sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class); | |
4164 | FuncDeclaration.syntaxCopy(sdd); | |
4165 | return sdd; | |
4166 | } | |
4167 | ||
4168 | override inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout | |
4169 | { | |
4170 | return this; | |
4171 | } | |
4172 | ||
4173 | override void accept(Visitor v) | |
4174 | { | |
4175 | v.visit(this); | |
4176 | } | |
4177 | } | |
4178 | ||
4179 | /*********************************************************** | |
4180 | */ | |
4181 | extern (C++) final class InvariantDeclaration : FuncDeclaration | |
4182 | { | |
4183 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id, Statement fbody) | |
4184 | { | |
b6df1132 | 4185 | // Make a unique invariant for now; we'll fix it up as we add it to the aggregate invariant list. |
5fee5ec3 IB |
4186 | super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null); |
4187 | this.fbody = fbody; | |
4188 | } | |
4189 | ||
4190 | override InvariantDeclaration syntaxCopy(Dsymbol s) | |
4191 | { | |
4192 | assert(!s); | |
4193 | auto id = new InvariantDeclaration(loc, endloc, storage_class, null, null); | |
4194 | FuncDeclaration.syntaxCopy(id); | |
4195 | return id; | |
4196 | } | |
4197 | ||
4198 | override bool isVirtual() const | |
4199 | { | |
4200 | return false; | |
4201 | } | |
4202 | ||
4203 | override bool addPreInvariant() | |
4204 | { | |
4205 | return false; | |
4206 | } | |
4207 | ||
4208 | override bool addPostInvariant() | |
4209 | { | |
4210 | return false; | |
4211 | } | |
4212 | ||
4213 | override inout(InvariantDeclaration) isInvariantDeclaration() inout | |
4214 | { | |
4215 | return this; | |
4216 | } | |
4217 | ||
4218 | override void accept(Visitor v) | |
4219 | { | |
4220 | v.visit(this); | |
4221 | } | |
b6df1132 IB |
4222 | |
4223 | extern (D) void fixupInvariantIdent(size_t offset) | |
4224 | { | |
4225 | OutBuffer idBuf; | |
4226 | idBuf.writestring("__invariant"); | |
4227 | idBuf.print(offset); | |
4228 | ||
4229 | ident = Identifier.idPool(idBuf[]); | |
4230 | } | |
5fee5ec3 IB |
4231 | } |
4232 | ||
4233 | ||
4234 | /*********************************************************** | |
4235 | */ | |
4236 | extern (C++) final class UnitTestDeclaration : FuncDeclaration | |
4237 | { | |
4238 | char* codedoc; // for documented unittest | |
4239 | ||
4240 | // toObjFile() these nested functions after this one | |
4241 | FuncDeclarations deferredNested; | |
4242 | ||
4243 | extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, char* codedoc) | |
4244 | { | |
4245 | super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null); | |
4246 | this.codedoc = codedoc; | |
4247 | } | |
4248 | ||
4249 | override UnitTestDeclaration syntaxCopy(Dsymbol s) | |
4250 | { | |
4251 | assert(!s); | |
4252 | auto utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc); | |
4253 | FuncDeclaration.syntaxCopy(utd); | |
4254 | return utd; | |
4255 | } | |
4256 | ||
4257 | override inout(AggregateDeclaration) isThis() inout | |
4258 | { | |
4259 | return null; | |
4260 | } | |
4261 | ||
4262 | override bool isVirtual() const | |
4263 | { | |
4264 | return false; | |
4265 | } | |
4266 | ||
4267 | override bool addPreInvariant() | |
4268 | { | |
4269 | return false; | |
4270 | } | |
4271 | ||
4272 | override bool addPostInvariant() | |
4273 | { | |
4274 | return false; | |
4275 | } | |
4276 | ||
4277 | override inout(UnitTestDeclaration) isUnitTestDeclaration() inout | |
4278 | { | |
4279 | return this; | |
4280 | } | |
4281 | ||
4282 | override void accept(Visitor v) | |
4283 | { | |
4284 | v.visit(this); | |
4285 | } | |
4286 | } | |
4287 | ||
4288 | /*********************************************************** | |
4289 | */ | |
4290 | extern (C++) final class NewDeclaration : FuncDeclaration | |
4291 | { | |
4292 | extern (D) this(const ref Loc loc, StorageClass stc) | |
4293 | { | |
4294 | super(loc, Loc.initial, Id.classNew, STC.static_ | stc, null); | |
4295 | } | |
4296 | ||
4297 | override NewDeclaration syntaxCopy(Dsymbol s) | |
4298 | { | |
4299 | assert(!s); | |
4300 | auto f = new NewDeclaration(loc, storage_class); | |
4301 | FuncDeclaration.syntaxCopy(f); | |
4302 | return f; | |
4303 | } | |
4304 | ||
4305 | override const(char)* kind() const | |
4306 | { | |
4307 | return "allocator"; | |
4308 | } | |
4309 | ||
4310 | override bool isVirtual() const | |
4311 | { | |
4312 | return false; | |
4313 | } | |
4314 | ||
4315 | override bool addPreInvariant() | |
4316 | { | |
4317 | return false; | |
4318 | } | |
4319 | ||
4320 | override bool addPostInvariant() | |
4321 | { | |
4322 | return false; | |
4323 | } | |
4324 | ||
4325 | override inout(NewDeclaration) isNewDeclaration() inout | |
4326 | { | |
4327 | return this; | |
4328 | } | |
4329 | ||
4330 | override void accept(Visitor v) | |
4331 | { | |
4332 | v.visit(this); | |
4333 | } | |
4334 | } | |
5eb9927a | 4335 | |
610d7898 IB |
4336 | /************************************** |
4337 | * A statement / expression in this scope is not `@safe`, | |
4338 | * so mark the enclosing function as `@system` | |
4339 | * | |
4340 | * Params: | |
4341 | * sc = scope that the unsafe statement / expression is in | |
4342 | * gag = surpress error message (used in escape.d) | |
4343 | * loc = location of error | |
4344 | * fmt = printf-style format string | |
4345 | * arg0 = (optional) argument for first %s format specifier | |
4346 | * arg1 = (optional) argument for second %s format specifier | |
445d8def | 4347 | * arg2 = (optional) argument for third %s format specifier |
610d7898 IB |
4348 | * Returns: whether there's a safe error |
4349 | */ | |
4350 | bool setUnsafe(Scope* sc, | |
445d8def IB |
4351 | bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, |
4352 | RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) | |
610d7898 IB |
4353 | { |
4354 | // TODO: | |
4355 | // For @system variables, unsafe initializers at global scope should mark | |
4356 | // the variable @system, see https://dlang.org/dips/1035 | |
4357 | ||
4358 | if (!sc.func) | |
4359 | return false; | |
4360 | ||
4361 | if (sc.intypeof) | |
4362 | return false; // typeof(cast(int*)0) is safe | |
4363 | ||
4364 | if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive | |
4365 | return false; | |
4366 | ||
4367 | if (sc.flags & SCOPE.compile) // __traits(compiles, x) | |
4368 | { | |
4369 | if (sc.func.isSafeBypassingInference()) | |
4370 | { | |
4371 | // Message wil be gagged, but still call error() to update global.errors and for | |
4372 | // -verrors=spec | |
445d8def | 4373 | .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); |
610d7898 IB |
4374 | return true; |
4375 | } | |
4376 | return false; | |
4377 | } | |
4378 | ||
445d8def | 4379 | return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); |
610d7898 IB |
4380 | } |
4381 | ||
7e7ebe3e IB |
4382 | /*************************************** |
4383 | * Like `setUnsafe`, but for safety errors still behind preview switches | |
4384 | * | |
4385 | * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, | |
4386 | * the behavior changes based on the setting: | |
4387 | * | |
4388 | * - In case of `-revert=fs`, it does nothing. | |
4389 | * - In case of `-preview=fs`, it's the same as `setUnsafe` | |
4390 | * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. | |
4391 | * | |
4392 | * Params: | |
4393 | * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope | |
4394 | * fs = feature state from the preview flag | |
4395 | * gag = surpress error message | |
4396 | * loc = location of error | |
4397 | * msg = printf-style format string | |
4398 | * arg0 = (optional) argument for first %s format specifier | |
4399 | * arg1 = (optional) argument for second %s format specifier | |
4400 | * arg2 = (optional) argument for third %s format specifier | |
4401 | * Returns: whether an actual safe error (not deprecation) occured | |
4402 | */ | |
4403 | bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, | |
4404 | RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) | |
4405 | { | |
4406 | if (fs == FeatureState.disabled) | |
4407 | { | |
4408 | return false; | |
4409 | } | |
4410 | else if (fs == FeatureState.enabled) | |
4411 | { | |
4412 | return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); | |
4413 | } | |
4414 | else | |
4415 | { | |
4416 | if (!sc.func) | |
4417 | return false; | |
4418 | if (sc.func.isSafeBypassingInference()) | |
4419 | { | |
4420 | if (!gag) | |
4421 | previewErrorFunc(sc.isDeprecated(), fs)( | |
4422 | loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "" | |
4423 | ); | |
4424 | } | |
4425 | else if (!sc.func.safetyViolation) | |
4426 | { | |
4427 | import dmd.func : AttributeViolation; | |
4428 | sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); | |
4429 | } | |
4430 | return false; | |
4431 | } | |
4432 | } | |
4433 | ||
5eb9927a IB |
4434 | /// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure` |
4435 | /// | |
4436 | /// Has two modes: | |
4437 | /// - a regular safety error, stored in (fmtStr, arg0, arg1) | |
4438 | /// - a call to a function without the attribute, which is a special case, because in that case, | |
4439 | /// that function might recursively also have a `AttributeViolation`. This way, in case | |
4440 | /// of a big call stack, the error can go down all the way to the root cause. | |
4441 | /// The `FunctionDeclaration` is then stored in `arg0` and `fmtStr` must be `null`. | |
610d7898 | 4442 | struct AttributeViolation |
5eb9927a IB |
4443 | { |
4444 | /// location of error | |
4445 | Loc loc = Loc.init; | |
4446 | /// printf-style format string | |
4447 | const(char)* fmtStr = null; | |
4448 | /// Arguments for up to two `%s` format specifiers in format string | |
4449 | RootObject arg0 = null; | |
4450 | /// ditto | |
4451 | RootObject arg1 = null; | |
445d8def IB |
4452 | /// ditto |
4453 | RootObject arg2 = null; | |
5eb9927a IB |
4454 | } |
4455 | ||
4456 | /// Print the reason why `fd` was inferred `@system` as a supplemental error | |
4457 | /// Params: | |
4458 | /// fd = function to check | |
4459 | /// maxDepth = up to how many functions deep to report errors | |
610d7898 IB |
4460 | /// deprecation = print deprecations instead of errors |
4461 | void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool deprecation) | |
5eb9927a | 4462 | { |
610d7898 | 4463 | auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; |
5eb9927a IB |
4464 | if (auto s = fd.safetyViolation) |
4465 | { | |
4466 | if (s.fmtStr) | |
4467 | { | |
610d7898 IB |
4468 | errorFunc(s.loc, deprecation ? |
4469 | "which would be `@system` because of:" : | |
4470 | "which was inferred `@system` because of:"); | |
445d8def IB |
4471 | errorFunc(s.loc, s.fmtStr, |
4472 | s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); | |
5eb9927a | 4473 | } |
b6df1132 | 4474 | else if (s.arg0.dyncast() == DYNCAST.dsymbol) |
5eb9927a | 4475 | { |
b6df1132 | 4476 | if (FuncDeclaration fd2 = (cast(Dsymbol) s.arg0).isFuncDeclaration()) |
5eb9927a | 4477 | { |
b6df1132 IB |
4478 | if (maxDepth > 0) |
4479 | { | |
4480 | errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); | |
4481 | errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); | |
4482 | } | |
5eb9927a IB |
4483 | } |
4484 | } | |
4485 | } | |
4486 | } |