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