/** * Defines a function declaration. * * Includes: * - function/delegate literals * - function aliases * - (static/shared) constructors/destructors/post-blits * - `invariant` * - `unittest` * * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/func.d, _func.d) * Documentation: https://dlang.org/phobos/dmd_func.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/func.d */ module dmd.func; import core.stdc.stdio; import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.blockexit; import dmd.gluelayer; import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.delegatize; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; import dmd.escape; import dmd.expression; import dmd.funcsem : overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; import dmd.location; import dmd.mtype; import dmd.objc; import dmd.common.outbuffer; import dmd.rootobject; import dmd.root.string; import dmd.root.stringtable; import dmd.statement; import dmd.tokens; import dmd.visitor; version (IN_GCC) {} else version (IN_LLVM) {} else version = MARS; /// Inline Status enum ILS : ubyte { uninitialized, /// not computed yet no, /// cannot inline yes, /// can inline } enum BUILTIN : ubyte { unknown = 255, /// not known if this is a builtin unimp = 0, /// this is not a builtin gcc, /// this is a GCC builtin llvm, /// this is an LLVM builtin sin, cos, tan, sqrt, fabs, ldexp, log, log2, log10, exp, expm1, exp2, round, floor, ceil, trunc, copysign, pow, fmin, fmax, fma, isnan, isinfinity, isfinite, bsf, bsr, bswap, popcnt, yl2x, yl2xp1, toPrecFloat, toPrecDouble, toPrecReal, ctfeWrite, } private struct FUNCFLAG { bool purityInprocess; /// working on determining purity bool safetyInprocess; /// working on determining safety bool nothrowInprocess; /// working on determining nothrow bool nogcInprocess; /// working on determining @nogc bool saferD; /// do -preview=safer checks if this function has default safety bool scopeInprocess; /// infer `return` and `scope` for parameters bool inlineScanned; /// function has been scanned for inline possibilities bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function bool scanf; /// is a scanf-like function bool noreturn; /// the function does not return bool isNRVO = true; /// Support for named return value optimization bool isNaked; /// The function is 'naked' (see inline ASM) bool isGenerated; /// The function is compiler generated (e.g. `opCmp`) bool isIntroducing; /// If this function introduces the overload set bool hasSemantic3Errors; /// If errors in semantic3 this function's frame ptr bool hasNoEH; /// No exception unwinding is needed bool inferRetType; /// Return type is to be inferred bool hasDualContext; /// has a dual-context 'this' parameter bool hasAlwaysInlines; /// Contains references to functions that must be inlined bool isCrtCtor; /// Has attribute pragma(crt_constructor) bool isCrtDtor; /// Has attribute pragma(crt_destructor) bool hasEscapingSiblings;/// Has sibling functions that escape bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed bool dllImport; /// __declspec(dllimport) bool dllExport; /// __declspec(dllexport) bool hasReturnExp; /// Has return exp; statement bool hasInlineAsm; /// Has asm{} statement bool hasMultipleReturnExp; /// Has multiple return exp; statements } /*********************************************************** * Tuple of result identifier (possibly null) and statement. * This is used to store out contracts: out(id){ ensure } */ extern (C++) struct Ensure { Identifier id; Statement ensure; Ensure syntaxCopy() { return Ensure(id, ensure.syntaxCopy()); } /***************************************** * Do syntax copy of an array of Ensure's. */ static Ensures* arraySyntaxCopy(Ensures* a) { if (!a) return null; Ensures* b = a.copy(); foreach (i, e; *a) (*b)[i] = e.syntaxCopy(); return b; } } /*********************************************************** * Most functions don't have contracts, so save memory by grouping * this information into a separate struct */ private struct ContractInfo { Statements* frequires; /// in contracts Ensures* fensures; /// out contracts Statement frequire; /// lowered in contract Statement fensure; /// lowered out contract FuncDeclaration fdrequire; /// function that does the in contract FuncDeclaration fdensure; /// function that does the out contract Expressions* fdrequireParams; /// argument list for __require Expressions* fdensureParams; /// argument list for __ensure } /*********************************************************** */ extern (C++) class FuncDeclaration : Declaration { Statement fbody; /// function body FuncDeclarations foverrides; /// functions this function overrides private ContractInfo* contracts; /// contract information const(char)* mangleString; /// mangled symbol created from mangleExact() VarDeclaration vresult; /// result variable for out contracts LabelDsymbol returnLabel; /// where the return goes bool[size_t] isTypeIsolatedCache; /// cache for the potentially very expensive isTypeIsolated check // used to prevent symbols in different // scopes from having the same name DsymbolTable localsymtab; VarDeclaration vthis; /// 'this' parameter (member and nested) VarDeclaration v_arguments; /// '_arguments' parameter VarDeclaration v_argptr; /// '_argptr' variable VarDeclarations* parameters; /// Array of VarDeclaration's for parameters DsymbolTable labtab; /// statement label symbol table Dsymbol overnext; /// next in overload list FuncDeclaration overnext0; /// next in overload list (only used during IFTI) Loc endloc; /// location of closing curly bracket int vtblIndex = -1; /// for member functions, index into vtbl[] ILS inlineStatusStmt = ILS.uninitialized; ILS inlineStatusExp = ILS.uninitialized; PINLINE inlining = PINLINE.default_; int inlineNest; /// !=0 if nested inline ForeachStatement fes; /// if foreach body, this is the foreach BaseClass* interfaceVirtual; /// if virtual, but only appears in base interface vtbl[] /** if !=NULL, then this is the type of the 'introducing' function this one is overriding */ Type tintro; STC storage_class2; /// storage class for template onemember's // Things that should really go into Scope VarDeclaration nrvo_var; /// variable to replace with shidden Symbol* shidden; /// hidden pointer passed to function ReturnStatements* returns; GotoStatements* gotos; /// Gotos with forward references version (MARS) { VarDeclarations* alignSectionVars; /// local variables with alignment needs larger than stackAlign Symbol* salignSection; /// pointer to aligned section, if any } /// set if this is a known, builtin function we can evaluate at compile time BUILTIN builtin = BUILTIN.unknown; /// set if someone took the address of this function int tookAddressOf; bool requiresClosure; // this function needs a closure /** local variables in this function which are referenced by nested functions * (They'll get put into the "closure" for this function.) */ VarDeclarations closureVars; /** Outer variables which are referenced by this nested function * (the inverse of closureVars) */ VarDeclarations outerVars; // Most recent encountered `main` (`WinMain` or `DllMain`) function. // Track it to give error messages for multiple entrypoints __gshared FuncDeclaration lastMain; /// Sibling nested functions which called this one FuncDeclarations siblingCallers; FuncDeclarations* inlinedNestedCallees; /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics AttributeViolation* safetyViolation; AttributeViolation* nogcViolation; AttributeViolation* pureViolation; AttributeViolation* nothrowViolation; /// See the `FUNCFLAG` struct import dmd.common.bitfields; mixin(generateBitFields!(FUNCFLAG, uint)); /** * Data for a function declaration that is needed for the Objective-C * integration. */ ObjcFuncDeclaration objc; extern (D) this(Loc loc, Loc endloc, Identifier ident, STC storage_class, Type type, bool noreturn = false) { super(DSYM.funcDeclaration, loc, ident); //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; this.type = type; if (type) { // Normalize storage_class, because function-type related attributes // are already set in the 'type' in parsing phase. this.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR); } this.endloc = endloc; if (noreturn) this.noreturn = true; /* The type given for "infer the return type" is a TypeFunction with * NULL for the return type. */ if (type && type.nextOf() is null) this.inferRetType = true; } static FuncDeclaration create(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) { return new FuncDeclaration(loc, endloc, id, cast(STC) storage_class, type, noreturn); } final nothrow pure @safe { private ref ContractInfo getContracts() { if (!contracts) contracts = new ContractInfo(); return *contracts; } // getters inout(Statements*) frequires() inout { return contracts ? contracts.frequires : null; } inout(Ensures*) fensures() inout { return contracts ? contracts.fensures : null; } inout(Statement) frequire() inout { return contracts ? contracts.frequire: null; } inout(Statement) fensure() inout { return contracts ? contracts.fensure : null; } inout(FuncDeclaration) fdrequire() inout { return contracts ? contracts.fdrequire : null; } inout(FuncDeclaration) fdensure() inout { return contracts ? contracts.fdensure: null; } inout(Expressions*) fdrequireParams() inout { return contracts ? contracts.fdrequireParams: null; } inout(Expressions*) fdensureParams() inout { return contracts ? contracts.fdensureParams: null; } extern (D) private static string generateContractSetter(string field, string type) { return type ~ " " ~ field ~ "(" ~ type ~ " param)" ~ "{ if (!param && !contracts) return null; return getContracts()." ~ field ~ " = param; }"; } mixin(generateContractSetter("frequires", "Statements*")); mixin(generateContractSetter("fensures", "Ensures*")); mixin(generateContractSetter("frequire", "Statement")); mixin(generateContractSetter("fensure", "Statement")); mixin(generateContractSetter("fdrequire", "FuncDeclaration")); mixin(generateContractSetter("fdensure", "FuncDeclaration")); mixin(generateContractSetter("fdrequireParams", "Expressions*")); mixin(generateContractSetter("fdensureParams", "Expressions*")); } override FuncDeclaration syntaxCopy(Dsymbol s) { //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); FuncDeclaration f = s ? cast(FuncDeclaration)s : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy(), this.noreturn != 0); f.frequires = frequires ? Statement.arraySyntaxCopy(frequires) : null; f.fensures = fensures ? Ensure.arraySyntaxCopy(fensures) : null; f.fbody = fbody ? fbody.syntaxCopy() : null; return f; } override final bool equals(const RootObject o) const { if (this == o) return true; auto s = isDsymbol(o); if (!s) return false; auto fd1 = this; auto fd2 = s.isFuncDeclaration(); if (!fd2) return false; auto fa1 = fd1.isFuncAliasDeclaration(); auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; auto fa2 = fd2.isFuncAliasDeclaration(); auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; if (fa1 && fa2) return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; bool b1 = fa1 !is null; if (b1 && faf1.isUnique() && !fa1.hasOverloads) b1 = false; bool b2 = fa2 !is null; if (b2 && faf2.isUnique() && !fa2.hasOverloads) b2 = false; if (b1 != b2) return false; return faf1.toParent().equals(faf2.toParent()) && faf1.ident.equals(faf2.ident) && faf1.type.equals(faf2.type); } /**************************************************** * Overload this FuncDeclaration with the new one f. * Return true if successful; i.e. no conflict. */ override bool overloadInsert(Dsymbol s) { //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars()); assert(s != this); if (AliasDeclaration ad = s.isAliasDeclaration()) { if (overnext) return overnext.overloadInsert(ad); if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof) { //printf("\tad = '%s'\n", ad.type.toChars()); return false; } overnext = ad; //printf("\ttrue: no conflict\n"); return true; } TemplateDeclaration td = s.isTemplateDeclaration(); if (td) { if (!td.funcroot) td.funcroot = this; if (overnext) return overnext.overloadInsert(td); overnext = td; return true; } FuncDeclaration fd = s.isFuncDeclaration(); if (!fd) return false; if (overnext) { td = overnext.isTemplateDeclaration(); if (td) fd.overloadInsert(td); else return overnext.overloadInsert(fd); } overnext = fd; //printf("\ttrue: no conflict\n"); return true; } /******************************************** * find function template root in overload list */ extern (D) final TemplateDeclaration findTemplateDeclRoot() { FuncDeclaration f = this; while (f && f.overnext) { //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars()); if (TemplateDeclaration td = f.overnext.isTemplateDeclaration()) return td; f = f.overnext.isFuncDeclaration(); } return null; } /******************************************** * Returns true if function was declared * directly or indirectly in a unittest block */ final bool inUnittest() { Dsymbol f = this; do { if (f.isUnitTestDeclaration()) return true; f = f.toParent(); } while (f); return false; } /******************************** * Searches for a label with the given identifier. This function will insert a new * `LabelDsymbol` into `labtab` if it does not contain a mapping for `ident`. * * Params: * ident = identifier of the requested label * loc = location used when creating a new `LabelDsymbol` * * Returns: the `LabelDsymbol` for `ident` */ final LabelDsymbol searchLabel(Identifier ident, Loc loc) { Dsymbol s; if (!labtab) labtab = new DsymbolTable(); // guess we need one s = labtab.lookup(ident); if (!s) { s = new LabelDsymbol(ident, loc); labtab.insert(s); } return cast(LabelDsymbol)s; } /***************************************** * Determine lexical level difference from `this` to nested function `fd`. * Params: * fd = target of call * intypeof = !=0 if inside typeof * Returns: * 0 same level * >0 decrease nesting by number * -1 increase nesting by 1 (`fd` is nested within `this`) * LevelError error, `this` cannot call `fd` */ extern (D) final int getLevel(FuncDeclaration fd, int intypeof) { //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars()); Dsymbol fdparent = fd.toParent2(); if (fdparent == this) return -1; Dsymbol s = this; int level = 0; while (fd != s && fdparent != s.toParent2()) { //printf("\ts = %s, '%s'\n", s.kind(), s.toChars()); if (auto thisfd = s.isFuncDeclaration()) { if (!thisfd.isNested() && !thisfd.vthis && !intypeof) return LevelError; } else { if (auto thiscd = s.isAggregateDeclaration()) { /* AggregateDeclaration::isNested returns true only when * it has a hidden pointer. * But, calling the function belongs unrelated lexical scope * is still allowed inside typeof. * * struct Map(alias fun) { * typeof({ return fun(); }) RetType; * // No member function makes Map struct 'not nested'. * } */ if (!thiscd.isNested() && !intypeof) return LevelError; } else return LevelError; } s = s.toParentP(fd); assert(s); level++; } return level; } enum LevelError = -2; override const(char)* toPrettyChars(bool QualifyTypes = false) { if (isMain()) return "D main"; return Dsymbol.toPrettyChars(QualifyTypes); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ final const(char)* toFullSignature() { OutBuffer buf; functionToBufferWithIdent(type.toTypeFunction(), buf, toChars(), isStatic); return buf.extractChars(); } final bool isMain() const { return ident == Id.main && resolvedLinkage() != LINK.c && !isMember() && !isNested(); } final bool isCMain() const { return ident == Id.main && resolvedLinkage() == LINK.c && !isMember() && !isNested(); } final bool isWinMain() const { //printf("FuncDeclaration::isWinMain() %s\n", toChars()); version (none) { bool x = ident == Id.WinMain && resolvedLinkage() != LINK.c && !isMember(); printf("%s\n", x ? "yes" : "no"); return x; } else { return ident == Id.WinMain && resolvedLinkage() != LINK.c && !isMember(); } } final bool isDllMain() const { return ident == Id.DllMain && resolvedLinkage() != LINK.c && !isMember(); } final bool isRtInit() const { return ident == Id.rt_init && resolvedLinkage() == LINK.c && !isMember() && !isNested(); } override final bool isExport() const { return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", visibility); return (visibility.kind == Visibility.Kind.export_ || dllImport) && !fbody; } override final bool isCodeseg() const pure nothrow @nogc @safe { return true; // functions are always in the code segment } override final bool isOverloadable() const { return true; // functions can be overloaded } /*********************************** * Override so it can work even if semantic() hasn't yet * been run. */ override final bool isAbstract() { if (storage_class & STC.abstract_) return true; if (semanticRun >= PASS.semanticdone) return false; if (_scope) { if (_scope.stc & STC.abstract_) return true; parent = _scope.parent; Dsymbol parent = toParent(); if (parent.isInterfaceDeclaration()) return true; } return false; } extern (D) final uint saveFlags() { return bitFields; } extern (D) final uint restoreFlags(uint f) { bitFields = f; return bitFields; } /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred * * Params: * loc = location of action * format = format string for error message * args = arguments to format string */ extern (D) final void setThrow(Loc loc, const(char)* format, RootObject[] args...) { if (nothrowInprocess && !nothrowViolation) { nothrowViolation = new AttributeViolation(loc, format, args); // action that requires GC } } /************************************** * The function calls non-`nothrow` function f, register that in case nothrow is being inferred * Params: * loc = location of call * fd = function being called */ extern (D) final void setThrowCall(Loc loc, FuncDeclaration fd) { if (nothrowInprocess && !nothrowViolation) { nothrowViolation = new AttributeViolation(loc, fd); // action that requires GC } } /**************************************** * Determine if function needs a static frame pointer. * Returns: * `true` if function is really nested within other function. * Contracts: * If isNested() returns true, isThis() should return false, * unless the function needs a dual-context pointer. */ bool isNested() const { auto f = toAliasFunc(); //printf("\ttoParent2() = '%s'\n", f.toParent2().toChars()); return ((f.storage_class & STC.static_) == 0) && (f._linkage == LINK.d) && (f.toParent2().isFuncDeclaration() !is null || f.toParent2() !is f.toParentLocal()); } /**************************************** * Determine if function is a non-static member function * that has an implicit 'this' expression. * Returns: * The aggregate it is a member of, or null. * Contracts: * Both isThis() and isNested() should return true if function needs a dual-context pointer, * otherwise if isThis() returns true, isNested() should return false. */ override inout(AggregateDeclaration) isThis() inout { //printf("+FuncDeclaration::isThis() '%s'\n", toChars()); auto ad = (storage_class & STC.static_) ? .objc.isThis(this) : isMemberLocal(); //printf("-FuncDeclaration::isThis() %p\n", ad); return ad; } override final bool needThis() { //printf("FuncDeclaration::needThis() '%s'\n", toChars()); return toAliasFunc().isThis() !is null; } // Determine if a function is pedantically virtual final bool isVirtualMethod() { if (toAliasFunc() != this) return toAliasFunc().isVirtualMethod(); //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars()); if (!isVirtual()) return false; // If it's a final method, and does not override anything, then it is not virtual if (isFinalFunc() && foverrides.length == 0) { return false; } return true; } // Determine if function goes into virtual function pointer table bool isVirtual() const { if (toAliasFunc() != this) return toAliasFunc().isVirtual(); auto p = toParent(); if (!isMember || !p.isClassDeclaration) return false; if (p.isClassDeclaration.classKind == ClassKind.objc) return .objc.isVirtual(this); version (none) { printf("FuncDeclaration::isVirtual(%s)\n", toChars()); printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), visibility == Visibility.Kind.private_, isCtorDeclaration(), linkage != LINK.d); printf("result is %d\n", isMember() && !(isStatic() || visibility == Visibility.Kind.private_ || visibility == Visibility.Kind.package_) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc())); } return !(isStatic() || visibility.kind == Visibility.Kind.private_ || visibility.kind == Visibility.Kind.package_) && !(p.isInterfaceDeclaration() && isFinalFunc()); } final bool isFinalFunc() const { if (toAliasFunc() != this) return toAliasFunc().isFinalFunc(); version (none) {{ auto cd = toParent().isClassDeclaration(); printf("FuncDeclaration::isFinalFunc(%s), %x\n", toChars(), Declaration.isFinal()); printf("%p %d %d %d\n", isMember(), isStatic(), Declaration.isFinal(), ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.final_)); printf("result is %d\n", isMember() && (Declaration.isFinal() || (cd !is null && cd.storage_class & STC.final_))); if (cd) printf("\tmember of %s\n", cd.toChars()); }} if (!isMember()) return false; if (Declaration.isFinal()) return true; auto cd = toParent().isClassDeclaration(); return (cd !is null) && (cd.storage_class & STC.final_); } bool addPreInvariant() { auto ad = isThis(); ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; 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()); } bool addPostInvariant() { auto ad = isThis(); ClassDeclaration cd = ad ? ad.isClassDeclaration() : null; 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()); } override const(char)* kind() const { return this.isGenerated() ? "generated function" : "function"; } /******************************************** * Returns: * true if there are no overloads of this function */ final bool isUnique() const { bool result = false; overloadApply(cast() this, (Dsymbol s) { auto f = s.isFuncDeclaration(); auto td = s.isTemplateDeclaration(); if (!f && !td) return 0; if (result) { result = false; return 1; // ambiguous, done } else { result = true; return 0; } }); return result; } /******************************* * Look at all the variables in this function that are referenced * by nested functions, and determine if a closure needs to be * created for them. */ final bool needsClosure() { /* Need a closure for all the closureVars[] if any of the * closureVars[] are accessed by a * function that escapes the scope of this function. * We take the conservative approach and decide that a function needs * a closure if it: * 1) is a virtual function * 2) has its address taken * 3) has a parent that escapes * 4) calls another nested function that needs a closure * * Note that since a non-virtual function can be called by * a virtual one, if that non-virtual function accesses a closure * var, the closure still has to be taken. Hence, we check for isThis() * instead of isVirtual(). (thanks to David Friedman) * * When the function returns a local struct or class, `requiresClosure` * is already set to `true` upon entering this function when the * struct/class refers to a local variable and a closure is needed. */ //printf("FuncDeclaration::needsClosure() %s\n", toPrettyChars()); if (requiresClosure) goto Lyes; for (size_t i = 0; i < closureVars.length; i++) { VarDeclaration v = closureVars[i]; //printf("\tv = %s\n", v.toChars()); for (size_t j = 0; j < v.nestedrefs.length; j++) { FuncDeclaration f = v.nestedrefs[j]; assert(f != this); /* __require and __ensure will always get called directly, * so they never make outer functions closure. */ if (f.ident == Id.require || f.ident == Id.ensure) continue; //printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf); /* Look to see if f escapes. We consider all parents of f within * this, and also all siblings which call f; if any of them escape, * so does f. * Mark all affected functions as requiring closures. */ for (Dsymbol s = f; s && s != this; s = s.toParentP(this)) { FuncDeclaration fx = s.isFuncDeclaration(); if (!fx) continue; if (fx.isThis() || fx.tookAddressOf) { //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf); /* Mark as needing closure any functions between this and f */ markAsNeedingClosure((fx == f) ? fx.toParentP(this) : fx, this); requiresClosure = true; } /* We also need to check if any sibling functions that * called us, have escaped. This is recursive: we need * to check the callers of our siblings. */ if (checkEscapingSiblings(fx, this)) requiresClosure = true; /* https://issues.dlang.org/show_bug.cgi?id=12406 * Iterate all closureVars to mark all descendant * nested functions that access to the closing context of this function. */ } } } if (requiresClosure) goto Lyes; return false; Lyes: return true; } /*********************************************** * Determine if function's variables are referenced by a function * nested within it. */ final bool hasNestedFrameRefs() { if (closureVars.length) return true; /* If a virtual function has contracts, assume its variables are referenced * by those contracts, even if they aren't. Because they might be referenced * by the overridden or overriding function's contracts. * This can happen because frequire and fensure are implemented as nested functions, * and they can be called directly by an overriding function and the overriding function's * context had better match, or * https://issues.dlang.org/show_bug.cgi?id=7335 will bite. */ if (fdrequire || fdensure) return true; if (foverrides.length && isVirtualMethod()) { for (size_t i = 0; i < foverrides.length; i++) { FuncDeclaration fdv = foverrides[i]; if (fdv.hasNestedFrameRefs()) return true; } } return false; } /********************************************* * Returns: the function's parameter list, and whether * it is variadic or not. */ final ParameterList getParameterList() { if (type) { if (TypeFunction fdtype = type.isTypeFunction()) // Could also be TypeError return fdtype.parameterList; } return ParameterList(null, VarArg.none); } /********************************** * Generate a FuncDeclaration for a runtime library function. */ extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none) { return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc); } extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none) { FuncDeclaration fd; TypeFunction tf; Dsymbol s; __gshared DsymbolTable st = null; //printf("genCfunc(name = '%s')\n", id.toChars()); //printf("treturn\n\t"); treturn.print(); // See if already in table if (!st) st = new DsymbolTable(); s = st.lookup(id); if (s) { fd = s.isFuncDeclaration(); assert(fd); assert(fd.type.nextOf().equals(treturn)); } else { tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc); fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf); fd.visibility = Visibility(Visibility.Kind.public_); fd._linkage = LINK.c; st.insert(fd); } return fd; } inout(FuncDeclaration) toAliasFunc() inout @safe { return this; } override void accept(Visitor v) { v.visit(this); } } /** Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the mismatching modifiers to `buf`. The modifiers of the `lhsMod` mismatching the ones with the `rhsMod` are printed, i.e. lhs(shared) vs. rhs() prints "`shared`", wheras lhs() vs rhs(shared) prints "non-shared". Params: buf = output buffer to write to lhsMod = modifier on the left-hand side lhsMod = modifier on the right-hand side Returns: A tuple with `isMutable` and `isNotShared` set if the `lhsMod` is missing those modifiers (compared to rhs). */ auto MODMatchToBuffer(OutBuffer* buf, ubyte lhsMod, ubyte rhsMod) { static struct Mismatches { bool isNotShared; bool isMutable; } Mismatches mismatches; bool bothMutable = ((lhsMod & rhsMod) == 0); bool sharedMismatch = ((lhsMod ^ rhsMod) & MODFlags.shared_) != 0; bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODFlags.shared_); if (lhsMod & MODFlags.shared_) buf.writestring("`shared` "); else if (sharedMismatch && !(lhsMod & MODFlags.immutable_)) { buf.writestring("non-shared "); mismatches.isNotShared = true; } if (bothMutable && sharedMismatchOnly) { } else if (lhsMod & MODFlags.immutable_) buf.writestring("`immutable` "); else if (lhsMod & MODFlags.const_) buf.writestring("`const` "); else if (lhsMod & MODFlags.wild) buf.writestring("`inout` "); else { buf.writestring("mutable "); mismatches.isMutable = true; } return mismatches; } /// unittest { OutBuffer buf; auto mismatches = MODMatchToBuffer(&buf, MODFlags.shared_, 0); assert(buf[] == "`shared` "); assert(!mismatches.isNotShared); buf.setsize(0); mismatches = MODMatchToBuffer(&buf, 0, MODFlags.shared_); assert(buf[] == "non-shared "); assert(mismatches.isNotShared); buf.setsize(0); mismatches = MODMatchToBuffer(&buf, MODFlags.const_, 0); assert(buf[] == "`const` "); assert(!mismatches.isMutable); buf.setsize(0); mismatches = MODMatchToBuffer(&buf, 0, MODFlags.const_); assert(buf[] == "mutable "); assert(mismatches.isMutable); } /* For all functions between outerFunc and f, mark them as needing * a closure. */ private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc) { for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.toParentP(outerFunc)) { FuncDeclaration fy = sx.isFuncDeclaration(); if (fy && fy.closureVars.length) { /* fy needs a closure if it has closureVars[], * because the frame pointer in the closure will be accessed. */ fy.requiresClosure = true; } } } /******** * Given a nested function f inside a function outerFunc, check * if any sibling callers of f have escaped. If so, mark * all the enclosing functions as needing closures. * This is recursive: we need to check the callers of our siblings. * Note that nested functions can only call lexically earlier nested * functions, so loops are impossible. * Params: * f = inner function (nested within outerFunc) * outerFunc = outer function * p = for internal recursion use * Returns: * true if any closures were needed */ bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) { static struct PrevSibling { PrevSibling* p; FuncDeclaration f; } if (f.computedEscapingSiblings) return f.hasEscapingSiblings; PrevSibling ps; ps.p = cast(PrevSibling*)p; ps.f = f; //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f.toChars(), outerFunc.toChars()); bool bAnyClosures = false; for (size_t i = 0; i < f.siblingCallers.length; ++i) { FuncDeclaration g = f.siblingCallers[i]; if (g.isThis() || g.tookAddressOf) { markAsNeedingClosure(g, outerFunc); bAnyClosures = true; } for (auto parent = g.toParentP(outerFunc); parent && parent !is outerFunc; parent = parent.toParentP(outerFunc)) { // A parent of the sibling had its address taken. // Assume escaping of parent affects its children, so needs propagating. // see https://issues.dlang.org/show_bug.cgi?id=19679 FuncDeclaration parentFunc = parent.isFuncDeclaration; if (parentFunc && parentFunc.tookAddressOf) { markAsNeedingClosure(parentFunc, outerFunc); bAnyClosures = true; } } PrevSibling* prev = cast(PrevSibling*)p; while (1) { if (!prev) { bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps); break; } if (prev.f == g) break; prev = prev.p; } } f.hasEscapingSiblings = bAnyClosures; f.computedEscapingSiblings = true; //printf("\t%d\n", bAnyClosures); return bAnyClosures; } /*********************************************************** * Used as a way to import a set of functions from another scope into this one. */ extern (C++) final class FuncAliasDeclaration : FuncDeclaration { FuncDeclaration funcalias; bool hasOverloads; extern (D) this(Identifier ident, FuncDeclaration funcalias, bool hasOverloads = true) { super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type); assert(funcalias != this); this.funcalias = funcalias; this.dsym = DSYM.funcAliasDeclaration; this.hasOverloads = hasOverloads; if (hasOverloads) { if (FuncAliasDeclaration fad = funcalias.isFuncAliasDeclaration()) this.hasOverloads = fad.hasOverloads; } else { // for internal use assert(!funcalias.isFuncAliasDeclaration()); this.hasOverloads = false; } userAttribDecl = funcalias.userAttribDecl; } override const(char)* kind() const { return "function alias"; } override inout(FuncDeclaration) toAliasFunc() inout { return funcalias.toAliasFunc(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration { TOK tok; // TOK.function_ or TOK.delegate_ Type treq; // target of return type inference // backend bool deferToObj; extern (D) this(Loc loc, Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, STC storage_class = STC.none) { super(loc, endloc, null, storage_class, type); this.dsym = DSYM.funcLiteralDeclaration; this.ident = id ? id : Id.empty; this.tok = tok; this.fes = fes; // Always infer scope for function literals // See https://issues.dlang.org/show_bug.cgi?id=20362 this.scopeInprocess = true; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars()); } override FuncLiteralDeclaration syntaxCopy(Dsymbol s) { //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars()); assert(!s); auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident, storage_class & STC.auto_); f.treq = treq; // don't need to copy FuncDeclaration.syntaxCopy(f); return f; } override bool isNested() const { //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars()); return (tok != TOK.function_) && !isThis(); } override inout(AggregateDeclaration) isThis() inout { return tok == TOK.delegate_ ? super.isThis() : null; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return false; } override const(char)* kind() const { // GCC requires the (char*) casts return (tok != TOK.function_) ? "delegate" : "function"; } override const(char)* toPrettyChars(bool QualifyTypes = false) { if (parent) { if (TemplateInstance ti = parent.isTemplateInstance()) return ti.tempdecl.toPrettyChars(QualifyTypes); } return Dsymbol.toPrettyChars(QualifyTypes); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class CtorDeclaration : FuncDeclaration { bool isCpCtor; // copy constructor bool isMoveCtor; // move constructor (aka rvalue constructor) extern (D) this(Loc loc, Loc endloc, STC stc, Type type) { super(loc, endloc, Id.ctor, stc, type); this.dsym = DSYM.ctorDeclaration; //printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this); } override CtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto f = new CtorDeclaration(loc, endloc, storage_class, type.syntaxCopy()); FuncDeclaration.syntaxCopy(f); return f; } override const(char)* kind() const { return isCpCtor ? "copy constructor" : "constructor"; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class PostBlitDeclaration : FuncDeclaration { extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); this.dsym = DSYM.postBlitDeclaration; } override PostBlitDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto dd = new PostBlitDeclaration(loc, endloc, storage_class, ident); FuncDeclaration.syntaxCopy(dd); return dd; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); } override bool overloadInsert(Dsymbol s) { return false; // cannot overload postblits } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class DtorDeclaration : FuncDeclaration { extern (D) this(Loc loc, Loc endloc) { super(loc, endloc, Id.dtor, STC.none, null); this.dsym = DSYM.dtorDeclaration; } extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); this.dsym = DSYM.dtorDeclaration; } override DtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto dd = new DtorDeclaration(loc, endloc, storage_class, ident); FuncDeclaration.syntaxCopy(dd); return dd; } override const(char)* kind() const { return "destructor"; } override bool isVirtual() const { // D dtor's don't get put into the vtbl[] // this is a hack so that extern(C++) destructors report as virtual, which are manually added to the vtable return vtblIndex != -1; } override bool addPreInvariant() { return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); } override bool addPostInvariant() { return false; } override bool overloadInsert(Dsymbol s) { return false; // cannot overload destructors } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class StaticCtorDeclaration : FuncDeclaration { extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null); this.dsym = DSYM.staticCtorDeclaration; } extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); this.dsym = DSYM.staticCtorDeclaration; } override StaticCtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto scd = new StaticCtorDeclaration(loc, endloc, storage_class); FuncDeclaration.syntaxCopy(scd); return scd; } override final inout(AggregateDeclaration) isThis() inout @nogc nothrow pure @safe { return null; } override final bool isVirtual() const @nogc nothrow pure @safe { return false; } override final bool addPreInvariant() @nogc nothrow pure @safe { return false; } override final bool addPostInvariant() @nogc nothrow pure @safe { return false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration { /// Exclude this constructor from cyclic dependency check bool standalone; extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticCtor", stc); this.dsym = DSYM.sharedStaticCtorDeclaration; } override SharedStaticCtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class); FuncDeclaration.syntaxCopy(scd); return scd; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class StaticDtorDeclaration : FuncDeclaration { VarDeclaration vgate; // 'gate' variable extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null); this.dsym = DSYM.staticDtorDeclaration; } extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); this.dsym = DSYM.staticDtorDeclaration; } override StaticDtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto sdd = new StaticDtorDeclaration(loc, endloc, storage_class); FuncDeclaration.syntaxCopy(sdd); return sdd; } override final inout(AggregateDeclaration) isThis() inout { return null; } override final bool isVirtual() const { return false; } override final bool addPreInvariant() { return false; } override final bool addPostInvariant() { return false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration { extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticDtor", stc); this.dsym = DSYM.sharedStaticDtorDeclaration; } override SharedStaticDtorDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class); FuncDeclaration.syntaxCopy(sdd); return sdd; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class InvariantDeclaration : FuncDeclaration { extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id, Statement fbody) { // Make a unique invariant for now; we'll fix it up as we add it to the aggregate invariant list. super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null); this.fbody = fbody; this.dsym = DSYM.invariantDeclaration; } override InvariantDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto id = new InvariantDeclaration(loc, endloc, storage_class, null, null); FuncDeclaration.syntaxCopy(id); return id; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return false; } override void accept(Visitor v) { v.visit(this); } extern (D) void fixupInvariantIdent(size_t offset) { OutBuffer idBuf; idBuf.writestring("__invariant"); idBuf.print(offset); ident = Identifier.idPool(idBuf[]); } } /*********************************************************** */ extern (C++) final class UnitTestDeclaration : FuncDeclaration { char* codedoc; // for documented unittest // toObjFile() these nested functions after this one FuncDeclarations deferredNested; extern (D) this(Loc loc, Loc endloc, STC stc, char* codedoc) { super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null); this.codedoc = codedoc; this.dsym = DSYM.unitTestDeclaration; } override UnitTestDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc); FuncDeclaration.syntaxCopy(utd); return utd; } override inout(AggregateDeclaration) isThis() inout { return null; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class NewDeclaration : FuncDeclaration { extern (D) this(Loc loc, STC stc) { super(loc, Loc.initial, Id.classNew, STC.static_ | stc, null); this.dsym = DSYM.newDeclaration; } override NewDeclaration syntaxCopy(Dsymbol s) { assert(!s); auto f = new NewDeclaration(loc, storage_class); FuncDeclaration.syntaxCopy(f); return f; } override const(char)* kind() const { return "allocator"; } override bool isVirtual() const { return false; } override bool addPreInvariant() { return false; } override bool addPostInvariant() { return false; } override void accept(Visitor v) { v.visit(this); } } /// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure` /// /// Has two modes: /// - a regular safety error, stored in `action` /// - a call to a function without the attribute, which is a special case, because in that case, /// that function might recursively also have a `AttributeViolation`. This way, in case /// of a big call stack, the error can go down all the way to the root cause. struct AttributeViolation { Loc loc; /// location of error FuncDeclaration fd; /// function is the focus of the violation // -- OR -- string action; /// Action that made the attribute fail to get inferred this(Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; } this(Loc loc, const(char)* fmt, RootObject[] args) { this.loc = loc; assert(args.length <= 4); // expand if necessary OutBuffer buf; buf.printf(fmt, args.length > 0 && args[0] ? args[0].toErrMsg() : "", args.length > 1 && args[1] ? args[1].toErrMsg() : "", args.length > 2 && args[2] ? args[2].toErrMsg() : "", args.length > 3 && args[3] ? args[3].toErrMsg() : "", ); this.action = buf.extractSlice(); } }