| /** | |
| * Performs the semantic3 stage, which deals with function bodies. | |
| * | |
| * Copyright: Copyright (C) 1999-2022 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/src/dmd/semantic3.d, _semantic3.d) | |
| * Documentation: https://dlang.org/phobos/dmd_semantic3.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic3.d | |
| */ | |
| module dmd.semantic3; | |
| import core.stdc.stdio; | |
| import core.stdc.string; | |
| import dmd.aggregate; | |
| import dmd.aliasthis; | |
| import dmd.arraytypes; | |
| import dmd.astcodegen; | |
| import dmd.astenums; | |
| import dmd.attrib; | |
| import dmd.blockexit; | |
| import dmd.clone; | |
| import dmd.ctorflow; | |
| import dmd.dcast; | |
| import dmd.dclass; | |
| import dmd.declaration; | |
| import dmd.denum; | |
| import dmd.dimport; | |
| import dmd.dinterpret; | |
| import dmd.dmodule; | |
| import dmd.dscope; | |
| import dmd.dstruct; | |
| import dmd.dsymbol; | |
| import dmd.dsymbolsem; | |
| import dmd.dtemplate; | |
| import dmd.dversion; | |
| import dmd.errors; | |
| import dmd.escape; | |
| import dmd.expression; | |
| import dmd.expressionsem; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.init; | |
| import dmd.initsem; | |
| import dmd.hdrgen; | |
| import dmd.mtype; | |
| import dmd.nogc; | |
| import dmd.nspace; | |
| import dmd.ob; | |
| import dmd.objc; | |
| import dmd.opover; | |
| import dmd.parse; | |
| import dmd.root.filename; | |
| import dmd.common.outbuffer; | |
| import dmd.root.rmem; | |
| import dmd.root.rootobject; | |
| import dmd.root.utf; | |
| import dmd.sideeffect; | |
| import dmd.statementsem; | |
| import dmd.staticassert; | |
| import dmd.tokens; | |
| import dmd.semantic2; | |
| import dmd.statement; | |
| import dmd.target; | |
| import dmd.templateparamsem; | |
| import dmd.typesem; | |
| import dmd.visitor; | |
| enum LOG = false; | |
| /************************************* | |
| * Does semantic analysis on function bodies. | |
| */ | |
| extern(C++) void semantic3(Dsymbol dsym, Scope* sc) | |
| { | |
| scope v = new Semantic3Visitor(sc); | |
| dsym.accept(v); | |
| } | |
| private extern(C++) final class Semantic3Visitor : Visitor | |
| { | |
| alias visit = Visitor.visit; | |
| Scope* sc; | |
| this(Scope* sc) | |
| { | |
| this.sc = sc; | |
| } | |
| override void visit(Dsymbol) {} | |
| override void visit(TemplateInstance tempinst) | |
| { | |
| static if (LOG) | |
| { | |
| printf("TemplateInstance.semantic3('%s'), semanticRun = %d\n", tempinst.toChars(), tempinst.semanticRun); | |
| } | |
| //if (toChars()[0] == 'D') *(char*)0=0; | |
| if (tempinst.semanticRun >= PASS.semantic3) | |
| return; | |
| tempinst.semanticRun = PASS.semantic3; | |
| if (tempinst.errors || !tempinst.members) | |
| return; | |
| TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration(); | |
| assert(tempdecl); | |
| sc = tempdecl._scope; | |
| sc = sc.push(tempinst.argsym); | |
| sc = sc.push(tempinst); | |
| sc.tinst = tempinst; | |
| sc.minst = tempinst.minst; | |
| int needGagging = (tempinst.gagged && !global.gag); | |
| uint olderrors = global.errors; | |
| int oldGaggedErrors = -1; // dead-store to prevent spurious warning | |
| /* If this is a gagged instantiation, gag errors. | |
| * Future optimisation: If the results are actually needed, errors | |
| * would already be gagged, so we don't really need to run semantic | |
| * on the members. | |
| */ | |
| if (needGagging) | |
| oldGaggedErrors = global.startGagging(); | |
| for (size_t i = 0; i < tempinst.members.dim; i++) | |
| { | |
| Dsymbol s = (*tempinst.members)[i]; | |
| s.semantic3(sc); | |
| if (tempinst.gagged && global.errors != olderrors) | |
| break; | |
| } | |
| if (global.errors != olderrors) | |
| { | |
| if (!tempinst.errors) | |
| { | |
| if (!tempdecl.literal) | |
| tempinst.error(tempinst.loc, "error instantiating"); | |
| if (tempinst.tinst) | |
| tempinst.tinst.printInstantiationTrace(); | |
| } | |
| tempinst.errors = true; | |
| } | |
| if (needGagging) | |
| global.endGagging(oldGaggedErrors); | |
| sc = sc.pop(); | |
| sc.pop(); | |
| } | |
| override void visit(TemplateMixin tmix) | |
| { | |
| if (tmix.semanticRun >= PASS.semantic3) | |
| return; | |
| tmix.semanticRun = PASS.semantic3; | |
| static if (LOG) | |
| { | |
| printf("TemplateMixin.semantic3('%s')\n", tmix.toChars()); | |
| } | |
| if (!tmix.members) | |
| return; | |
| sc = sc.push(tmix.argsym); | |
| sc = sc.push(tmix); | |
| for (size_t i = 0; i < tmix.members.dim; i++) | |
| { | |
| Dsymbol s = (*tmix.members)[i]; | |
| s.semantic3(sc); | |
| } | |
| sc = sc.pop(); | |
| sc.pop(); | |
| } | |
| override void visit(Module mod) | |
| { | |
| //printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent); | |
| if (mod.semanticRun != PASS.semantic2done) | |
| return; | |
| mod.semanticRun = PASS.semantic3; | |
| // Note that modules get their own scope, from scratch. | |
| // This is so regardless of where in the syntax a module | |
| // gets imported, it is unaffected by context. | |
| Scope* sc = Scope.createGlobal(mod); // create root scope | |
| //printf("Module = %p\n", sc.scopesym); | |
| if (mod.members) | |
| { | |
| // Pass 3 semantic routines: do initializers and function bodies | |
| for (size_t i = 0; i < mod.members.dim; i++) | |
| { | |
| Dsymbol s = (*mod.members)[i]; | |
| //printf("Module %s: %s.semantic3()\n", toChars(), s.toChars()); | |
| s.semantic3(sc); | |
| mod.runDeferredSemantic2(); | |
| } | |
| } | |
| if (mod.userAttribDecl) | |
| { | |
| mod.userAttribDecl.semantic3(sc); | |
| } | |
| sc = sc.pop(); | |
| sc.pop(); | |
| mod.semanticRun = PASS.semantic3done; | |
| } | |
| override void visit(FuncDeclaration funcdecl) | |
| { | |
| //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc); | |
| /* Determine if function should add `return 0;` | |
| */ | |
| bool addReturn0() | |
| { | |
| //printf("addReturn0()\n"); | |
| auto f = funcdecl.type.isTypeFunction(); | |
| // C11 5.1.2.2.3 | |
| if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32) | |
| return true; | |
| return f.next.ty == Tvoid && | |
| (funcdecl.isMain() || global.params.betterC && funcdecl.isCMain()); | |
| } | |
| VarDeclaration _arguments = null; | |
| if (!funcdecl.parent) | |
| { | |
| if (global.errors) | |
| return; | |
| //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc); | |
| assert(0); | |
| } | |
| if (funcdecl.errors || isError(funcdecl.parent)) | |
| { | |
| funcdecl.errors = true; | |
| // Mark that the return type could not be inferred | |
| if (funcdecl.inferRetType) | |
| { | |
| assert(funcdecl.type); | |
| auto tf = funcdecl.type.isTypeFunction(); | |
| // Only change the return type s.t. other analysis is | |
| // still possible e.g. missmatched parameter types | |
| if (tf && !tf.next) | |
| tf.next = Type.terror; | |
| } | |
| return; | |
| } | |
| //printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", funcdecl.parent.toChars(), funcdecl.toChars(), funcdecl, sc, funcdecl.loc.toChars()); | |
| //fflush(stdout); | |
| //printf("storage class = x%x %x\n", sc.stc, storage_class); | |
| //{ static int x; if (++x == 2) *(char*)0=0; } | |
| //printf("\tlinkage = %d\n", sc.linkage); | |
| if (funcdecl.ident == Id.assign && !funcdecl.inuse) | |
| { | |
| if (funcdecl.storage_class & STC.inference) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=15044 | |
| * For generated opAssign function, any errors | |
| * from its body need to be gagged. | |
| */ | |
| uint oldErrors = global.startGagging(); | |
| ++funcdecl.inuse; | |
| funcdecl.semantic3(sc); | |
| --funcdecl.inuse; | |
| if (global.endGagging(oldErrors)) // if errors happened | |
| { | |
| // Disable generated opAssign, because some members forbid identity assignment. | |
| funcdecl.storage_class |= STC.disable; | |
| funcdecl.fbody = null; // remove fbody which contains the error | |
| funcdecl.flags &= ~FUNCFLAG.semantic3Errors; | |
| } | |
| return; | |
| } | |
| } | |
| //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract)); | |
| if (funcdecl.semanticRun >= PASS.semantic3) | |
| return; | |
| funcdecl.semanticRun = PASS.semantic3; | |
| funcdecl.flags &= ~FUNCFLAG.semantic3Errors; | |
| if (!funcdecl.type || funcdecl.type.ty != Tfunction) | |
| return; | |
| TypeFunction f = cast(TypeFunction)funcdecl.type; | |
| if (!funcdecl.inferRetType && f.next.ty == Terror) | |
| return; | |
| if (!funcdecl.fbody && funcdecl.inferRetType && !f.next) | |
| { | |
| funcdecl.error("has no function body with return type inference"); | |
| return; | |
| } | |
| uint oldErrors = global.errors; | |
| auto fds = FuncDeclSem3(funcdecl,sc); | |
| fds.checkInContractOverrides(); | |
| // Remember whether we need to generate an 'out' contract. | |
| immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl); | |
| if (funcdecl.fbody || funcdecl.frequires || needEnsure) | |
| { | |
| /* Symbol table into which we place parameters and nested functions, | |
| * solely to diagnose name collisions. | |
| */ | |
| funcdecl.localsymtab = new DsymbolTable(); | |
| // Establish function scope | |
| auto ss = new ScopeDsymbol(funcdecl.loc, null); | |
| // find enclosing scope symbol, might skip symbol-less CTFE and/or FuncExp scopes | |
| ss.parent = sc.inner().scopesym; | |
| ss.endlinnum = funcdecl.endloc.linnum; | |
| Scope* sc2 = sc.push(ss); | |
| sc2.func = funcdecl; | |
| sc2.parent = funcdecl; | |
| sc2.ctorflow.callSuper = CSX.none; | |
| sc2.sbreak = null; | |
| sc2.scontinue = null; | |
| sc2.sw = null; | |
| sc2.fes = funcdecl.fes; | |
| sc2.linkage = LINK.d; | |
| sc2.stc &= STC.flowThruFunction; | |
| sc2.visibility = Visibility(Visibility.Kind.public_); | |
| sc2.explicitVisibility = 0; | |
| sc2.aligndecl = null; | |
| if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) | |
| sc2.flags = sc.flags & ~SCOPE.contract; | |
| sc2.flags &= ~SCOPE.compile; | |
| sc2.tf = null; | |
| sc2.os = null; | |
| sc2.inLoop = false; | |
| sc2.userAttribDecl = null; | |
| if (sc2.intypeof == 1) | |
| sc2.intypeof = 2; | |
| sc2.ctorflow.fieldinit = null; | |
| /* Note: When a lambda is defined immediately under aggregate member | |
| * scope, it should be contextless due to prevent interior pointers. | |
| * e.g. | |
| * // dg points 'this' - its interior pointer | |
| * class C { int x; void delegate() dg = (){ this.x = 1; }; } | |
| * | |
| * However, lambdas could be used inside typeof, in order to check | |
| * some expressions validity at compile time. For such case the lambda | |
| * body can access aggregate instance members. | |
| * e.g. | |
| * class C { int x; static assert(is(typeof({ this.x = 1; }))); } | |
| * | |
| * To properly accept it, mark these lambdas as member functions. | |
| */ | |
| if (auto fld = funcdecl.isFuncLiteralDeclaration()) | |
| { | |
| if (auto ad = funcdecl.isMember2()) | |
| { | |
| if (!sc.intypeof) | |
| { | |
| if (fld.tok == TOK.delegate_) | |
| funcdecl.error("cannot be %s members", ad.kind()); | |
| else | |
| fld.tok = TOK.function_; | |
| } | |
| else | |
| { | |
| if (fld.tok != TOK.function_) | |
| fld.tok = TOK.delegate_; | |
| } | |
| } | |
| } | |
| funcdecl.declareThis(sc2); | |
| // Reverts: https://issues.dlang.org/show_bug.cgi?id=5710 | |
| // No compiler supports this, and there was never any spec for it. | |
| // @@@DEPRECATED_2.116@@@ | |
| // Deprecated in 2.096, can be made an error in 2.116. | |
| // The deprecation period is longer than usual as dual-context | |
| // functions may be widely used by dmd-compiled projects. | |
| // It also gives more time for the implementation of dual-context | |
| // functions to be reworked as a frontend-only feature. | |
| if (funcdecl.hasDualContext()) | |
| { | |
| funcdecl.deprecation("function requires a dual-context, which is deprecated"); | |
| if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null) | |
| ti.printInstantiationTrace(Classification.deprecation); | |
| } | |
| //printf("[%s] ad = %p vthis = %p\n", loc.toChars(), ad, vthis); | |
| //if (vthis) printf("\tvthis.type = %s\n", vthis.type.toChars()); | |
| // Declare hidden variable _arguments[] and _argptr | |
| if (f.parameterList.varargs == VarArg.variadic) | |
| { | |
| if (f.linkage == LINK.d) | |
| { | |
| // Variadic arguments depend on Typeinfo being defined. | |
| if (!global.params.useTypeInfo || !Type.dtypeinfo || !Type.typeinfotypelist) | |
| { | |
| if (!global.params.useTypeInfo) | |
| funcdecl.error("D-style variadic functions cannot be used with -betterC"); | |
| else if (!Type.typeinfotypelist) | |
| funcdecl.error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions"); | |
| else | |
| funcdecl.error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions"); | |
| fatal(); | |
| } | |
| // Declare _arguments[] | |
| funcdecl.v_arguments = new VarDeclaration(funcdecl.loc, Type.typeinfotypelist.type, Id._arguments_typeinfo, null); | |
| funcdecl.v_arguments.storage_class |= STC.temp | STC.parameter; | |
| funcdecl.v_arguments.dsymbolSemantic(sc2); | |
| sc2.insert(funcdecl.v_arguments); | |
| funcdecl.v_arguments.parent = funcdecl; | |
| //Type t = Type.dtypeinfo.type.constOf().arrayOf(); | |
| Type t = Type.dtypeinfo.type.arrayOf(); | |
| _arguments = new VarDeclaration(funcdecl.loc, t, Id._arguments, null); | |
| _arguments.storage_class |= STC.temp; | |
| _arguments.dsymbolSemantic(sc2); | |
| sc2.insert(_arguments); | |
| _arguments.parent = funcdecl; | |
| } | |
| if (f.linkage == LINK.d || f.parameterList.length) | |
| { | |
| // Declare _argptr | |
| Type t = target.va_listType(funcdecl.loc, sc); | |
| // Init is handled in FuncDeclaration_toObjFile | |
| funcdecl.v_argptr = new VarDeclaration(funcdecl.loc, t, Id._argptr, new VoidInitializer(funcdecl.loc)); | |
| funcdecl.v_argptr.storage_class |= STC.temp; | |
| funcdecl.v_argptr.dsymbolSemantic(sc2); | |
| sc2.insert(funcdecl.v_argptr); | |
| funcdecl.v_argptr.parent = funcdecl; | |
| } | |
| } | |
| /* Declare all the function parameters as variables | |
| * and install them in parameters[] | |
| */ | |
| if (const nparams = f.parameterList.length) | |
| { | |
| /* parameters[] has all the tuples removed, as the back end | |
| * doesn't know about tuples | |
| */ | |
| funcdecl.parameters = new VarDeclarations(); | |
| funcdecl.parameters.reserve(nparams); | |
| foreach (i, fparam; f.parameterList) | |
| { | |
| Identifier id = fparam.ident; | |
| StorageClass stc = 0; | |
| if (!id) | |
| { | |
| /* Generate identifier for un-named parameter, | |
| * because we need it later on. | |
| */ | |
| fparam.ident = id = Identifier.generateId("_param_", i); | |
| stc |= STC.temp; | |
| } | |
| Type vtype = fparam.type; | |
| auto v = new VarDeclaration(funcdecl.loc, vtype, id, null); | |
| //printf("declaring parameter %s of type %s\n", v.toChars(), v.type.toChars()); | |
| stc |= STC.parameter; | |
| if (f.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) | |
| { | |
| stc |= STC.variadic; | |
| auto vtypeb = vtype.toBasetype(); | |
| if (vtypeb.ty == Tarray || vtypeb.ty == Tclass) | |
| { | |
| /* Since it'll be pointing into the stack for the array | |
| * contents, it needs to be `scope` | |
| */ | |
| stc |= STC.scope_; | |
| } | |
| } | |
| if ((funcdecl.flags & FUNCFLAG.inferScope) && !(fparam.storageClass & STC.scope_)) | |
| stc |= STC.maybescope; | |
| stc |= fparam.storageClass & (STC.IOR | STC.return_ | STC.scope_ | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor | STC.returnScope); | |
| v.storage_class = stc; | |
| v.dsymbolSemantic(sc2); | |
| if (!sc2.insert(v)) | |
| { | |
| funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars()); | |
| funcdecl.errors = true; | |
| } | |
| else | |
| funcdecl.parameters.push(v); | |
| funcdecl.localsymtab.insert(v); | |
| v.parent = funcdecl; | |
| if (fparam.userAttribDecl) | |
| v.userAttribDecl = fparam.userAttribDecl; | |
| } | |
| } | |
| // Declare the tuple symbols and put them in the symbol table, | |
| // but not in parameters[]. | |
| if (f.parameterList.parameters) | |
| foreach (fparam; *f.parameterList.parameters) | |
| { | |
| if (!fparam.ident) | |
| continue; // never used, so ignore | |
| // expand any tuples | |
| if (fparam.type.ty != Ttuple) | |
| continue; | |
| TypeTuple t = cast(TypeTuple)fparam.type; | |
| size_t dim = Parameter.dim(t.arguments); | |
| auto exps = new Objects(dim); | |
| foreach (j; 0 .. dim) | |
| { | |
| Parameter narg = Parameter.getNth(t.arguments, j); | |
| assert(narg.ident); | |
| VarDeclaration v = sc2.search(Loc.initial, narg.ident, null).isVarDeclaration(); | |
| assert(v); | |
| (*exps)[j] = new VarExp(v.loc, v); | |
| } | |
| assert(fparam.ident); | |
| auto v = new TupleDeclaration(funcdecl.loc, fparam.ident, exps); | |
| //printf("declaring tuple %s\n", v.toChars()); | |
| v.isexp = true; | |
| if (!sc2.insert(v)) | |
| funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars()); | |
| funcdecl.localsymtab.insert(v); | |
| v.parent = funcdecl; | |
| } | |
| // Precondition invariant | |
| Statement fpreinv = null; | |
| if (funcdecl.addPreInvariant()) | |
| { | |
| Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); | |
| if (e) | |
| fpreinv = new ExpStatement(Loc.initial, e); | |
| } | |
| // Postcondition invariant | |
| Statement fpostinv = null; | |
| if (funcdecl.addPostInvariant()) | |
| { | |
| Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); | |
| if (e) | |
| fpostinv = new ExpStatement(Loc.initial, e); | |
| } | |
| // Pre/Postcondition contract | |
| if (!funcdecl.fbody) | |
| funcdecl.buildEnsureRequire(); | |
| Scope* scout = null; | |
| if (needEnsure || funcdecl.addPostInvariant()) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=3657 | |
| * Set the correct end line number for fensure scope. | |
| */ | |
| uint fensure_endlin = funcdecl.endloc.linnum; | |
| if (funcdecl.fensure) | |
| if (auto s = funcdecl.fensure.isScopeStatement()) | |
| fensure_endlin = s.endloc.linnum; | |
| if ((needEnsure && global.params.useOut == CHECKENABLE.on) || fpostinv) | |
| { | |
| funcdecl.returnLabel = funcdecl.searchLabel(Id.returnLabel); | |
| } | |
| // scope of out contract (need for vresult.semantic) | |
| auto sym = new ScopeDsymbol(funcdecl.loc, null); | |
| sym.parent = sc2.scopesym; | |
| sym.endlinnum = fensure_endlin; | |
| scout = sc2.push(sym); | |
| } | |
| if (funcdecl.fbody) | |
| { | |
| auto sym = new ScopeDsymbol(funcdecl.loc, null); | |
| sym.parent = sc2.scopesym; | |
| sym.endlinnum = funcdecl.endloc.linnum; | |
| sc2 = sc2.push(sym); | |
| auto ad2 = funcdecl.isMemberLocal(); | |
| /* If this is a class constructor | |
| */ | |
| if (ad2 && funcdecl.isCtorDeclaration()) | |
| { | |
| sc2.ctorflow.allocFieldinit(ad2.fields.dim); | |
| foreach (v; ad2.fields) | |
| { | |
| v.ctorinit = 0; | |
| } | |
| } | |
| bool inferRef = (f.isref && (funcdecl.storage_class & STC.auto_)); | |
| funcdecl.fbody = funcdecl.fbody.statementSemantic(sc2); | |
| if (!funcdecl.fbody) | |
| funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements()); | |
| if (funcdecl.isNaked()) | |
| { | |
| fpreinv = null; // can't accommodate with no stack frame | |
| fpostinv = null; | |
| } | |
| assert(funcdecl.type == f || (funcdecl.type.ty == Tfunction && f.purity == PURE.impure && (cast(TypeFunction)funcdecl.type).purity >= PURE.fwdref)); | |
| f = cast(TypeFunction)funcdecl.type; | |
| if (funcdecl.inferRetType) | |
| { | |
| // If no return type inferred yet, then infer a void | |
| if (!f.next) | |
| f.next = Type.tvoid; | |
| if (f.checkRetType(funcdecl.loc)) | |
| funcdecl.fbody = new ErrorStatement(); | |
| else | |
| funcdecl.checkMain(); // Check main() parameters and return type | |
| } | |
| if (f.next !is null) | |
| f.next.checkComplexTransition(funcdecl.loc, sc); | |
| if (funcdecl.returns && !funcdecl.fbody.isErrorStatement()) | |
| { | |
| for (size_t i = 0; i < funcdecl.returns.dim;) | |
| { | |
| Expression exp = (*funcdecl.returns)[i].exp; | |
| if (exp.op == EXP.variable && (cast(VarExp)exp).var == funcdecl.vresult) | |
| { | |
| if (addReturn0()) | |
| exp.type = Type.tint32; | |
| else | |
| exp.type = f.next; | |
| // Remove `return vresult;` from returns | |
| funcdecl.returns.remove(i); | |
| continue; | |
| } | |
| if (inferRef && f.isref && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 | |
| f.isref = false; | |
| i++; | |
| } | |
| } | |
| if (f.isref) // Function returns a reference | |
| { | |
| if (funcdecl.storage_class & STC.auto_) | |
| funcdecl.storage_class &= ~STC.auto_; | |
| } | |
| // handle NRVO | |
| if (!target.isReturnOnStack(f, funcdecl.needThis()) || !funcdecl.checkNRVO()) | |
| funcdecl.flags &= ~FUNCFLAG.NRVO; | |
| if (funcdecl.fbody.isErrorStatement()) | |
| { | |
| } | |
| else if (funcdecl.isStaticCtorDeclaration()) | |
| { | |
| /* It's a static constructor. Ensure that all | |
| * ctor consts were initialized. | |
| */ | |
| ScopeDsymbol pd = funcdecl.toParent().isScopeDsymbol(); | |
| for (size_t i = 0; i < pd.members.dim; i++) | |
| { | |
| Dsymbol s = (*pd.members)[i]; | |
| s.checkCtorConstInit(); | |
| } | |
| } | |
| else if (ad2 && funcdecl.isCtorDeclaration()) | |
| { | |
| ClassDeclaration cd = ad2.isClassDeclaration(); | |
| // Verify that all the ctorinit fields got initialized | |
| if (!(sc2.ctorflow.callSuper & CSX.this_ctor)) | |
| { | |
| foreach (i, v; ad2.fields) | |
| { | |
| if (v.isThisDeclaration()) | |
| continue; | |
| if (v.ctorinit == 0) | |
| { | |
| /* Current bugs in the flow analysis: | |
| * 1. union members should not produce error messages even if | |
| * not assigned to | |
| * 2. structs should recognize delegating opAssign calls as well | |
| * as delegating calls to other constructors | |
| */ | |
| if (v.isCtorinit() && !v.type.isMutable() && cd) | |
| funcdecl.error("missing initializer for %s field `%s`", MODtoChars(v.type.mod), v.toChars()); | |
| else if (v.storage_class & STC.nodefaultctor) | |
| error(funcdecl.loc, "field `%s` must be initialized in constructor", v.toChars()); | |
| else if (v.type.needsNested()) | |
| error(funcdecl.loc, "field `%s` must be initialized in constructor, because it is nested struct", v.toChars()); | |
| } | |
| else | |
| { | |
| bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); | |
| if (mustInit && !(sc2.ctorflow.fieldinit[i].csx & CSX.this_ctor)) | |
| { | |
| funcdecl.error("field `%s` must be initialized but skipped", v.toChars()); | |
| } | |
| } | |
| } | |
| } | |
| sc2.ctorflow.freeFieldinit(); | |
| if (cd && !(sc2.ctorflow.callSuper & CSX.any_ctor) && cd.baseClass && cd.baseClass.ctor) | |
| { | |
| sc2.ctorflow.callSuper = CSX.none; | |
| // Insert implicit super() at start of fbody | |
| Type tthis = ad2.type.addMod(funcdecl.vthis.type.mod); | |
| FuncDeclaration fd = resolveFuncCall(Loc.initial, sc2, cd.baseClass.ctor, null, tthis, null, FuncResolveFlag.quiet); | |
| if (!fd) | |
| { | |
| funcdecl.error("no match for implicit `super()` call in constructor"); | |
| } | |
| else if (fd.storage_class & STC.disable) | |
| { | |
| funcdecl.error("cannot call `super()` implicitly because it is annotated with `@disable`"); | |
| } | |
| else | |
| { | |
| Expression e1 = new SuperExp(Loc.initial); | |
| Expression e = new CallExp(Loc.initial, e1); | |
| e = e.expressionSemantic(sc2); | |
| Statement s = new ExpStatement(Loc.initial, e); | |
| funcdecl.fbody = new CompoundStatement(Loc.initial, s, funcdecl.fbody); | |
| } | |
| } | |
| //printf("ctorflow.callSuper = x%x\n", sc2.ctorflow.callSuper); | |
| } | |
| /* https://issues.dlang.org/show_bug.cgi?id=17502 | |
| * Wait until after the return type has been inferred before | |
| * generating the contracts for this function, and merging contracts | |
| * from overrides. | |
| * | |
| * https://issues.dlang.org/show_bug.cgi?id=17893 | |
| * However should take care to generate this before inferered | |
| * function attributes are applied, such as 'nothrow'. | |
| * | |
| * This was originally at the end of the first semantic pass, but | |
| * required a fix-up to be done here for the '__result' variable | |
| * type of __ensure() inside auto functions, but this didn't work | |
| * if the out parameter was implicit. | |
| */ | |
| funcdecl.buildEnsureRequire(); | |
| // Check for errors related to 'nothrow'. | |
| const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isnothrow); | |
| if (f.isnothrow && blockexit & BE.throw_) | |
| error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); | |
| if (!(blockexit & (BE.throw_ | BE.halt) || funcdecl.flags & FUNCFLAG.hasCatches)) | |
| { | |
| /* Don't generate unwind tables for this function | |
| * https://issues.dlang.org/show_bug.cgi?id=17997 | |
| */ | |
| funcdecl.flags |= FUNCFLAG.noEH; | |
| } | |
| if (funcdecl.flags & FUNCFLAG.nothrowInprocess) | |
| { | |
| if (funcdecl.type == f) | |
| f = cast(TypeFunction)f.copy(); | |
| f.isnothrow = !(blockexit & BE.throw_); | |
| } | |
| if (funcdecl.fbody.isErrorStatement()) | |
| { | |
| } | |
| else if (ad2 && funcdecl.isCtorDeclaration()) | |
| { | |
| /* Append: | |
| * return this; | |
| * to function body | |
| */ | |
| if (blockexit & BE.fallthru) | |
| { | |
| Statement s = new ReturnStatement(funcdecl.loc, null); | |
| s = s.statementSemantic(sc2); | |
| funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s); | |
| funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); | |
| } | |
| } | |
| else if (funcdecl.fes) | |
| { | |
| // For foreach(){} body, append a return 0; | |
| if (blockexit & BE.fallthru) | |
| { | |
| Expression e = IntegerExp.literal!0; | |
| Statement s = new ReturnStatement(Loc.initial, e); | |
| funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); | |
| funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); | |
| } | |
| assert(!funcdecl.returnLabel); | |
| } | |
| else if (f.next.toBasetype().ty == Tnoreturn) | |
| { | |
| // Fallthrough despite being declared as noreturn? return is already rejected when evaluating the ReturnStatement | |
| if (blockexit & BE.fallthru) | |
| { | |
| funcdecl.error("is typed as `%s` but does return", f.next.toChars()); | |
| funcdecl.loc.errorSupplemental("`noreturn` functions must either throw, abort or loop indefinitely"); | |
| } | |
| } | |
| else | |
| { | |
| const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; | |
| if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile)) | |
| { | |
| if (!funcdecl.hasReturnExp) | |
| funcdecl.error("has no `return` statement, but is expected to return a value of type `%s`", f.next.toChars()); | |
| else | |
| funcdecl.error("no `return exp;` or `assert(0);` at end of function"); | |
| } | |
| } | |
| if (funcdecl.returns) | |
| { | |
| bool implicit0 = addReturn0(); | |
| Type tret = implicit0 ? Type.tint32 : f.next; | |
| assert(tret.ty != Tvoid); | |
| if (funcdecl.vresult || funcdecl.returnLabel) | |
| funcdecl.buildResultVar(scout ? scout : sc2, tret); | |
| /* Cannot move this loop into NrvoWalker, because | |
| * returns[i] may be in the nested delegate for foreach-body. | |
| */ | |
| for (size_t i = 0; i < funcdecl.returns.dim; i++) | |
| { | |
| ReturnStatement rs = (*funcdecl.returns)[i]; | |
| Expression exp = rs.exp; | |
| if (exp.op == EXP.error) | |
| continue; | |
| if (tret.ty == Terror) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=13702 | |
| exp = checkGC(sc2, exp); | |
| continue; | |
| } | |
| /* If the expression in the return statement (exp) cannot be implicitly | |
| * converted to the return type (tret) of the function and if the | |
| * type of the expression is type isolated, then it may be possible | |
| * that a promotion to `immutable` or `inout` (through a cast) will | |
| * match the return type. | |
| */ | |
| if (!exp.implicitConvTo(tret) && funcdecl.isTypeIsolated(exp.type)) | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=20073 | |
| * | |
| * The problem is that if the type of the returned expression (exp.type) | |
| * is an aggregated declaration with an alias this, the alias this may be | |
| * used for the conversion testing without it being an isolated type. | |
| * | |
| * To make sure this does not happen, we can test here the implicit conversion | |
| * only for the aggregated declaration type by using `implicitConvToWithoutAliasThis`. | |
| * The implicit conversion with alias this is taken care of later. | |
| */ | |
| AggregateDeclaration aggDecl = isAggregate(exp.type); | |
| TypeStruct tstruct; | |
| TypeClass tclass; | |
| bool hasAliasThis; | |
| if (aggDecl && aggDecl.aliasthis) | |
| { | |
| hasAliasThis = true; | |
| tclass = exp.type.isTypeClass(); | |
| if (!tclass) | |
| tstruct = exp.type.isTypeStruct(); | |
| assert(tclass || tstruct); | |
| } | |
| if (hasAliasThis) | |
| { | |
| if (tclass) | |
| { | |
| if ((cast(TypeClass)(exp.type.immutableOf())).implicitConvToWithoutAliasThis(tret)) | |
| exp = exp.castTo(sc2, exp.type.immutableOf()); | |
| else if ((cast(TypeClass)(exp.type.wildOf())).implicitConvToWithoutAliasThis(tret)) | |
| exp = exp.castTo(sc2, exp.type.wildOf()); | |
| } | |
| else | |
| { | |
| if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret)) | |
| exp = exp.castTo(sc2, exp.type.immutableOf()); | |
| else if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret)) | |
| exp = exp.castTo(sc2, exp.type.wildOf()); | |
| } | |
| } | |
| else | |
| { | |
| if (exp.type.immutableOf().implicitConvTo(tret)) | |
| exp = exp.castTo(sc2, exp.type.immutableOf()); | |
| else if (exp.type.wildOf().implicitConvTo(tret)) | |
| exp = exp.castTo(sc2, exp.type.wildOf()); | |
| } | |
| } | |
| const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; | |
| // if a copy constructor is present, the return type conversion will be handled by it | |
| if (!(hasCopyCtor && exp.isLvalue())) | |
| { | |
| if (f.isref && !MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) | |
| error(exp.loc, "expression `%s` of type `%s` is not implicitly convertible to return type `ref %s`", | |
| exp.toChars(), exp.type.toChars(), tret.toChars()); | |
| else | |
| exp = exp.implicitCastTo(sc2, tret); | |
| } | |
| if (f.isref) | |
| { | |
| // Function returns a reference | |
| exp = exp.toLvalue(sc2, exp); | |
| checkReturnEscapeRef(sc2, exp, false); | |
| exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); | |
| } | |
| else | |
| { | |
| exp = exp.optimize(WANTvalue); | |
| /* https://issues.dlang.org/show_bug.cgi?id=10789 | |
| * If NRVO is not possible, all returned lvalues should call their postblits. | |
| */ | |
| if (!funcdecl.isNRVO()) | |
| exp = doCopyOrMove(sc2, exp, f.next); | |
| if (tret.hasPointers()) | |
| checkReturnEscape(sc2, exp, false); | |
| } | |
| exp = checkGC(sc2, exp); | |
| if (funcdecl.vresult) | |
| { | |
| // Create: return vresult = exp; | |
| exp = new BlitExp(rs.loc, funcdecl.vresult, exp); | |
| exp.type = funcdecl.vresult.type; | |
| if (rs.caseDim) | |
| exp = Expression.combine(exp, new IntegerExp(rs.caseDim)); | |
| } | |
| else if (funcdecl.tintro && !tret.equals(funcdecl.tintro.nextOf())) | |
| { | |
| exp = exp.implicitCastTo(sc2, funcdecl.tintro.nextOf()); | |
| } | |
| rs.exp = exp; | |
| } | |
| } | |
| if (funcdecl.nrvo_var || funcdecl.returnLabel) | |
| { | |
| scope NrvoWalker nw = new NrvoWalker(); | |
| nw.fd = funcdecl; | |
| nw.sc = sc2; | |
| nw.visitStmt(funcdecl.fbody); | |
| } | |
| sc2 = sc2.pop(); | |
| } | |
| if (global.params.inclusiveInContracts) | |
| { | |
| funcdecl.frequire = funcdecl.mergeFrequireInclusivePreview( | |
| funcdecl.frequire, funcdecl.fdrequireParams); | |
| } | |
| else | |
| { | |
| funcdecl.frequire = funcdecl.mergeFrequire(funcdecl.frequire, funcdecl.fdrequireParams); | |
| } | |
| funcdecl.fensure = funcdecl.mergeFensure(funcdecl.fensure, Id.result, funcdecl.fdensureParams); | |
| Statement freq = funcdecl.frequire; | |
| Statement fens = funcdecl.fensure; | |
| /* Do the semantic analysis on the [in] preconditions and | |
| * [out] postconditions. | |
| */ | |
| if (freq) | |
| { | |
| /* frequire is composed of the [in] contracts | |
| */ | |
| auto sym = new ScopeDsymbol(funcdecl.loc, null); | |
| sym.parent = sc2.scopesym; | |
| sym.endlinnum = funcdecl.endloc.linnum; | |
| sc2 = sc2.push(sym); | |
| sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require; | |
| // BUG: need to error if accessing out parameters | |
| // BUG: need to disallow returns and throws | |
| // BUG: verify that all in and ref parameters are read | |
| freq = freq.statementSemantic(sc2); | |
| freq.blockExit(funcdecl, false); | |
| funcdecl.flags &= ~FUNCFLAG.noEH; | |
| sc2 = sc2.pop(); | |
| if (global.params.useIn == CHECKENABLE.off) | |
| freq = null; | |
| } | |
| if (fens) | |
| { | |
| /* fensure is composed of the [out] contracts | |
| */ | |
| if (f.next.ty == Tvoid && funcdecl.fensures) | |
| { | |
| foreach (e; *funcdecl.fensures) | |
| { | |
| if (e.id) | |
| { | |
| funcdecl.error(e.ensure.loc, "`void` functions have no result"); | |
| //fens = null; | |
| } | |
| } | |
| } | |
| sc2 = scout; //push | |
| sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; | |
| // BUG: need to disallow returns and throws | |
| if (funcdecl.fensure && f.next.ty != Tvoid) | |
| funcdecl.buildResultVar(scout, f.next); | |
| fens = fens.statementSemantic(sc2); | |
| fens.blockExit(funcdecl, false); | |
| funcdecl.flags &= ~FUNCFLAG.noEH; | |
| sc2 = sc2.pop(); | |
| if (global.params.useOut == CHECKENABLE.off) | |
| fens = null; | |
| } | |
| if (funcdecl.fbody && funcdecl.fbody.isErrorStatement()) | |
| { | |
| } | |
| else | |
| { | |
| auto a = new Statements(); | |
| // Merge in initialization of 'out' parameters | |
| if (funcdecl.parameters) | |
| { | |
| for (size_t i = 0; i < funcdecl.parameters.dim; i++) | |
| { | |
| VarDeclaration v = (*funcdecl.parameters)[i]; | |
| if (v.storage_class & STC.out_) | |
| { | |
| if (!v._init) | |
| { | |
| v.error("Zero-length `out` parameters are not allowed."); | |
| return; | |
| } | |
| ExpInitializer ie = v._init.isExpInitializer(); | |
| assert(ie); | |
| if (auto iec = ie.exp.isConstructExp()) | |
| { | |
| // construction occurred in parameter processing | |
| auto ec = new AssignExp(iec.loc, iec.e1, iec.e2); | |
| ec.type = iec.type; | |
| ie.exp = ec; | |
| } | |
| a.push(new ExpStatement(Loc.initial, ie.exp)); | |
| } | |
| } | |
| } | |
| if (_arguments) | |
| { | |
| /* Advance to elements[] member of TypeInfo_Tuple with: | |
| * _arguments = v_arguments.elements; | |
| */ | |
| Expression e = new VarExp(Loc.initial, funcdecl.v_arguments); | |
| e = new DotIdExp(Loc.initial, e, Id.elements); | |
| e = new ConstructExp(Loc.initial, _arguments, e); | |
| e = e.expressionSemantic(sc2); | |
| _arguments._init = new ExpInitializer(Loc.initial, e); | |
| auto de = new DeclarationExp(Loc.initial, _arguments); | |
| a.push(new ExpStatement(Loc.initial, de)); | |
| } | |
| // Merge contracts together with body into one compound statement | |
| if (freq || fpreinv) | |
| { | |
| if (!freq) | |
| freq = fpreinv; | |
| else if (fpreinv) | |
| freq = new CompoundStatement(Loc.initial, freq, fpreinv); | |
| a.push(freq); | |
| } | |
| if (funcdecl.fbody) | |
| a.push(funcdecl.fbody); | |
| if (fens || fpostinv) | |
| { | |
| if (!fens) | |
| fens = fpostinv; | |
| else if (fpostinv) | |
| fens = new CompoundStatement(Loc.initial, fpostinv, fens); | |
| auto ls = new LabelStatement(Loc.initial, Id.returnLabel, fens); | |
| funcdecl.returnLabel.statement = ls; | |
| a.push(funcdecl.returnLabel.statement); | |
| if (f.next.ty != Tvoid && funcdecl.vresult) | |
| { | |
| // Create: return vresult; | |
| Expression e = new VarExp(Loc.initial, funcdecl.vresult); | |
| if (funcdecl.tintro) | |
| { | |
| e = e.implicitCastTo(sc, funcdecl.tintro.nextOf()); | |
| e = e.expressionSemantic(sc); | |
| } | |
| auto s = new ReturnStatement(Loc.initial, e); | |
| a.push(s); | |
| } | |
| } | |
| if (addReturn0()) | |
| { | |
| // Add a return 0; statement | |
| Statement s = new ReturnStatement(Loc.initial, IntegerExp.literal!0); | |
| a.push(s); | |
| } | |
| Statement sbody = new CompoundStatement(Loc.initial, a); | |
| /* Append destructor calls for parameters as finally blocks. | |
| */ | |
| if (funcdecl.parameters) | |
| { | |
| // check if callee destroys arguments | |
| const bool paramsNeedDtor = target.isCalleeDestroyingArgs(f); | |
| foreach (v; *funcdecl.parameters) | |
| { | |
| if (v.isReference() || (v.storage_class & STC.lazy_)) | |
| continue; | |
| if (v.needsScopeDtor()) | |
| { | |
| v.storage_class |= STC.nodtor; | |
| if (!paramsNeedDtor) | |
| continue; | |
| // same with ExpStatement.scopeCode() | |
| Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); | |
| s = s.statementSemantic(sc2); | |
| immutable bool isnothrow = f.isnothrow && !(funcdecl.flags & FUNCFLAG.nothrowInprocess); | |
| const blockexit = s.blockExit(funcdecl, isnothrow); | |
| if (blockexit & BE.throw_) | |
| { | |
| funcdecl.flags &= ~FUNCFLAG.noEH; | |
| if (isnothrow) | |
| error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); | |
| else if (funcdecl.flags & FUNCFLAG.nothrowInprocess) | |
| f.isnothrow = false; | |
| } | |
| if (sbody.blockExit(funcdecl, f.isnothrow) == BE.fallthru) | |
| sbody = new CompoundStatement(Loc.initial, sbody, s); | |
| else | |
| sbody = new TryFinallyStatement(Loc.initial, sbody, s); | |
| } | |
| } | |
| } | |
| // from this point on all possible 'throwers' are checked | |
| funcdecl.flags &= ~FUNCFLAG.nothrowInprocess; | |
| if (funcdecl.isSynchronized()) | |
| { | |
| /* Wrap the entire function body in a synchronized statement | |
| */ | |
| ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration(); | |
| if (cd) | |
| { | |
| if (target.libraryObjectMonitors(funcdecl, sbody)) | |
| { | |
| Expression vsync; | |
| if (funcdecl.isStatic()) | |
| { | |
| // The monitor is in the ClassInfo | |
| vsync = new DotIdExp(funcdecl.loc, symbolToExp(cd, funcdecl.loc, sc2, false), Id.classinfo); | |
| } | |
| else | |
| { | |
| // 'this' is the monitor | |
| vsync = new VarExp(funcdecl.loc, funcdecl.vthis); | |
| if (funcdecl.hasDualContext()) | |
| { | |
| vsync = new PtrExp(funcdecl.loc, vsync); | |
| vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0); | |
| } | |
| } | |
| sbody = new PeelStatement(sbody); // don't redo semantic() | |
| sbody = new SynchronizedStatement(funcdecl.loc, vsync, sbody); | |
| sbody = sbody.statementSemantic(sc2); | |
| } | |
| } | |
| else | |
| { | |
| funcdecl.error("synchronized function `%s` must be a member of a class", funcdecl.toChars()); | |
| } | |
| } | |
| // If declaration has no body, don't set sbody to prevent incorrect codegen. | |
| if (funcdecl.fbody || funcdecl.allowsContractWithoutBody()) | |
| funcdecl.fbody = sbody; | |
| } | |
| // Check for undefined labels | |
| if (funcdecl.labtab) | |
| foreach (keyValue; funcdecl.labtab.tab.asRange) | |
| { | |
| //printf(" KV: %s = %s\n", keyValue.key.toChars(), keyValue.value.toChars()); | |
| LabelDsymbol label = cast(LabelDsymbol)keyValue.value; | |
| if (!label.statement && (!label.deleted || label.iasm)) | |
| { | |
| funcdecl.error(label.loc, "label `%s` is undefined", label.toChars()); | |
| } | |
| } | |
| // Fix up forward-referenced gotos | |
| if (funcdecl.gotos && !funcdecl.isCsymbol()) | |
| { | |
| for (size_t i = 0; i < funcdecl.gotos.dim; ++i) | |
| { | |
| (*funcdecl.gotos)[i].checkLabel(); | |
| } | |
| } | |
| if (funcdecl.isNaked() && (funcdecl.fensures || funcdecl.frequires)) | |
| funcdecl.error("naked assembly functions with contracts are not supported"); | |
| sc2.ctorflow.callSuper = CSX.none; | |
| sc2.pop(); | |
| } | |
| if (funcdecl.checkClosure()) | |
| { | |
| // We should be setting errors here instead of relying on the global error count. | |
| //errors = true; | |
| } | |
| /* If function survived being marked as impure, then it is pure | |
| */ | |
| if (funcdecl.flags & FUNCFLAG.purityInprocess) | |
| { | |
| funcdecl.flags &= ~FUNCFLAG.purityInprocess; | |
| if (funcdecl.type == f) | |
| f = cast(TypeFunction)f.copy(); | |
| f.purity = PURE.fwdref; | |
| } | |
| if (funcdecl.flags & FUNCFLAG.safetyInprocess) | |
| { | |
| funcdecl.flags &= ~FUNCFLAG.safetyInprocess; | |
| if (funcdecl.type == f) | |
| f = cast(TypeFunction)f.copy(); | |
| f.trust = TRUST.safe; | |
| } | |
| if (funcdecl.flags & FUNCFLAG.nogcInprocess) | |
| { | |
| funcdecl.flags &= ~FUNCFLAG.nogcInprocess; | |
| if (funcdecl.type == f) | |
| f = cast(TypeFunction)f.copy(); | |
| f.isnogc = true; | |
| } | |
| if (funcdecl.flags & FUNCFLAG.returnInprocess) | |
| { | |
| funcdecl.flags &= ~FUNCFLAG.returnInprocess; | |
| if (funcdecl.storage_class & STC.return_) | |
| { | |
| if (funcdecl.type == f) | |
| f = cast(TypeFunction)f.copy(); | |
| f.isreturn = true; | |
| f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope); | |
| if (funcdecl.storage_class & STC.returninferred) | |
| f.isreturninferred = true; | |
| } | |
| } | |
| funcdecl.flags &= ~FUNCFLAG.inferScope; | |
| // Eliminate maybescope's | |
| { | |
| // Create and fill array[] with maybe candidates from the `this` and the parameters | |
| VarDeclaration[10] tmp = void; | |
| size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0); | |
| import dmd.common.string : SmallBuffer; | |
| auto sb = SmallBuffer!VarDeclaration(dim, tmp[]); | |
| VarDeclaration[] array = sb[]; | |
| size_t n = 0; | |
| if (funcdecl.vthis) | |
| array[n++] = funcdecl.vthis; | |
| if (funcdecl.parameters) | |
| { | |
| foreach (v; *funcdecl.parameters) | |
| { | |
| array[n++] = v; | |
| } | |
| } | |
| eliminateMaybeScopes(array[0 .. n]); | |
| } | |
| // Infer STC.scope_ | |
| if (funcdecl.parameters && !funcdecl.errors) | |
| { | |
| assert(f.parameterList.length == funcdecl.parameters.dim); | |
| foreach (u, p; f.parameterList) | |
| { | |
| auto v = (*funcdecl.parameters)[u]; | |
| if (v.storage_class & STC.maybescope) | |
| { | |
| //printf("Inferring scope for %s\n", v.toChars()); | |
| notMaybeScope(v); | |
| v.storage_class |= STC.scope_ | STC.scopeinferred; | |
| p.storageClass |= STC.scope_ | STC.scopeinferred; | |
| assert(!(p.storageClass & STC.maybescope)); | |
| } | |
| } | |
| } | |
| if (funcdecl.vthis && funcdecl.vthis.storage_class & STC.maybescope) | |
| { | |
| notMaybeScope(funcdecl.vthis); | |
| funcdecl.vthis.storage_class |= STC.scope_ | STC.scopeinferred; | |
| f.isScopeQual = true; | |
| f.isscopeinferred = true; | |
| } | |
| // reset deco to apply inference result to mangled name | |
| if (f != funcdecl.type) | |
| f.deco = null; | |
| // Do semantic type AFTER pure/nothrow inference. | |
| if (!f.deco && funcdecl.ident != Id.xopEquals && funcdecl.ident != Id.xopCmp) | |
| { | |
| sc = sc.push(); | |
| if (funcdecl.isCtorDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=#15665 | |
| f.isctor = true; | |
| sc.stc = 0; | |
| sc.linkage = funcdecl.linkage; // https://issues.dlang.org/show_bug.cgi?id=8496 | |
| funcdecl.type = f.typeSemantic(funcdecl.loc, sc); | |
| sc = sc.pop(); | |
| } | |
| // Do live analysis | |
| if (global.params.useDIP1021 && funcdecl.fbody && funcdecl.type.ty != Terror && | |
| funcdecl.type.isTypeFunction().islive) | |
| { | |
| oblive(funcdecl); | |
| } | |
| /* If this function had instantiated with gagging, error reproduction will be | |
| * done by TemplateInstance::semantic. | |
| * Otherwise, error gagging should be temporarily ungagged by functionSemantic3. | |
| */ | |
| funcdecl.semanticRun = PASS.semantic3done; | |
| if ((global.errors != oldErrors) || (funcdecl.fbody && funcdecl.fbody.isErrorStatement())) | |
| funcdecl.flags |= FUNCFLAG.semantic3Errors; | |
| else | |
| funcdecl.flags &= ~FUNCFLAG.semantic3Errors; | |
| if (funcdecl.type.ty == Terror) | |
| funcdecl.errors = true; | |
| //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent.toChars(), toChars(), sc, loc.toChars()); | |
| //fflush(stdout); | |
| } | |
| override void visit(CtorDeclaration ctor) | |
| { | |
| //printf("CtorDeclaration()\n%s\n", ctor.fbody.toChars()); | |
| if (ctor.semanticRun >= PASS.semantic3) | |
| return; | |
| /* If any of the fields of the aggregate have a destructor, add | |
| * scope (failure) { this.fieldDtor(); } | |
| * as the first statement of the constructor (unless the constructor | |
| * doesn't define a body - @disable, extern) | |
| *.It is not necessary to add it after | |
| * each initialization of a field, because destruction of .init constructed | |
| * structs should be benign. | |
| * https://issues.dlang.org/show_bug.cgi?id=14246 | |
| */ | |
| AggregateDeclaration ad = ctor.isMemberDecl(); | |
| if (!ctor.fbody || !ad || !ad.fieldDtor || !global.params.dtorFields || global.params.betterC || ctor.type.toTypeFunction.isnothrow) | |
| return visit(cast(FuncDeclaration)ctor); | |
| /* Generate: | |
| * this.fieldDtor() | |
| */ | |
| Expression e = new ThisExp(ctor.loc); | |
| e.type = ad.type.mutableOf(); | |
| e = new DotVarExp(ctor.loc, e, ad.fieldDtor, false); | |
| auto ce = new CallExp(ctor.loc, e); | |
| auto sexp = new ExpStatement(ctor.loc, ce); | |
| auto ss = new ScopeStatement(ctor.loc, sexp, ctor.loc); | |
| // @@@DEPRECATED_2.106@@@ | |
| // Allow negligible attribute violations to allow for a smooth | |
| // transition. Remove this after the usual deprecation period | |
| // after 2.106. | |
| if (global.params.dtorFields == FeatureState.default_) | |
| { | |
| auto ctf = cast(TypeFunction) ctor.type; | |
| auto dtf = cast(TypeFunction) ad.fieldDtor.type; | |
| const ngErr = ctf.isnogc && !dtf.isnogc; | |
| const puErr = ctf.purity && !dtf.purity; | |
| const saErr = ctf.trust == TRUST.safe && dtf.trust <= TRUST.system; | |
| if (ngErr || puErr || saErr) | |
| { | |
| // storage_class is apparently not set for dtor & ctor | |
| OutBuffer ob; | |
| stcToBuffer(&ob, | |
| (ngErr ? STC.nogc : 0) | | |
| (puErr ? STC.pure_ : 0) | | |
| (saErr ? STC.system : 0) | |
| ); | |
| ctor.loc.deprecation("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars()); | |
| ctor.loc.deprecationSupplemental("The destructor will be called if an exception is thrown"); | |
| ctor.loc.deprecationSupplemental("Either make the constructor `nothrow` or adjust the field destructors"); | |
| ce.ignoreAttributes = true; | |
| } | |
| } | |
| version (all) | |
| { | |
| /* Generate: | |
| * try { ctor.fbody; } | |
| * catch (Exception __o) | |
| * { this.fieldDtor(); throw __o; } | |
| * This differs from the alternate scope(failure) version in that an Exception | |
| * is caught rather than a Throwable. This enables the optimization whereby | |
| * the try-catch can be removed if ctor.fbody is nothrow. (nothrow only | |
| * applies to Exception.) | |
| */ | |
| Identifier id = Identifier.generateId("__o"); | |
| auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id)); | |
| auto handler = new CompoundStatement(ctor.loc, ss, ts); | |
| auto catches = new Catches(); | |
| auto ctch = new Catch(ctor.loc, getException(), id, handler); | |
| catches.push(ctch); | |
| ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches); | |
| } | |
| else | |
| { | |
| /* Generate: | |
| * scope (failure) { this.fieldDtor(); } | |
| * Hopefully we can use this version someday when scope(failure) catches | |
| * Exception instead of Throwable. | |
| */ | |
| auto s = new ScopeGuardStatement(ctor.loc, TOK.onScopeFailure, ss); | |
| ctor.fbody = new CompoundStatement(ctor.loc, s, ctor.fbody); | |
| } | |
| visit(cast(FuncDeclaration)ctor); | |
| } | |
| override void visit(Nspace ns) | |
| { | |
| if (ns.semanticRun >= PASS.semantic3) | |
| return; | |
| ns.semanticRun = PASS.semantic3; | |
| static if (LOG) | |
| { | |
| printf("Nspace::semantic3('%s')\n", ns.toChars()); | |
| } | |
| if (!ns.members) | |
| return; | |
| sc = sc.push(ns); | |
| sc.linkage = LINK.cpp; | |
| foreach (s; *ns.members) | |
| { | |
| s.semantic3(sc); | |
| } | |
| sc.pop(); | |
| } | |
| override void visit(AttribDeclaration ad) | |
| { | |
| Dsymbols* d = ad.include(sc); | |
| if (!d) | |
| return; | |
| Scope* sc2 = ad.newScope(sc); | |
| for (size_t i = 0; i < d.dim; i++) | |
| { | |
| Dsymbol s = (*d)[i]; | |
| s.semantic3(sc2); | |
| } | |
| if (sc2 != sc) | |
| sc2.pop(); | |
| } | |
| override void visit(AggregateDeclaration ad) | |
| { | |
| //printf("AggregateDeclaration::semantic3(sc=%p, %s) type = %s, errors = %d\n", sc, toChars(), type.toChars(), errors); | |
| if (!ad.members) | |
| return; | |
| StructDeclaration sd = ad.isStructDeclaration(); | |
| if (!sc) // from runDeferredSemantic3 for TypeInfo generation | |
| { | |
| assert(sd); | |
| sd.semanticTypeInfoMembers(); | |
| return; | |
| } | |
| auto sc2 = ad.newScope(sc); | |
| for (size_t i = 0; i < ad.members.dim; i++) | |
| { | |
| Dsymbol s = (*ad.members)[i]; | |
| s.semantic3(sc2); | |
| } | |
| sc2.pop(); | |
| // don't do it for unused deprecated types | |
| // or error ypes | |
| if (!ad.getRTInfo && Type.rtinfo && (!ad.isDeprecated() || global.params.useDeprecated != DiagnosticReporting.error) && (ad.type && ad.type.ty != Terror)) | |
| { | |
| // Evaluate: RTinfo!type | |
| auto tiargs = new Objects(); | |
| tiargs.push(ad.type); | |
| auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs); | |
| Scope* sc3 = ti.tempdecl._scope.startCTFE(); | |
| sc3.tinst = sc.tinst; | |
| sc3.minst = sc.minst; | |
| if (ad.isDeprecated()) | |
| sc3.stc |= STC.deprecated_; | |
| ti.dsymbolSemantic(sc3); | |
| ti.semantic2(sc3); | |
| ti.semantic3(sc3); | |
| auto e = symbolToExp(ti.toAlias(), Loc.initial, sc3, false); | |
| sc3.endCTFE(); | |
| e = e.ctfeInterpret(); | |
| ad.getRTInfo = e; | |
| } | |
| if (sd) | |
| sd.semanticTypeInfoMembers(); | |
| ad.semanticRun = PASS.semantic3done; | |
| } | |
| } | |
| private struct FuncDeclSem3 | |
| { | |
| // The FuncDeclaration subject to Semantic analysis | |
| FuncDeclaration funcdecl; | |
| // Scope of analysis | |
| Scope* sc; | |
| this(FuncDeclaration fd,Scope* s) | |
| { | |
| funcdecl = fd; | |
| sc = s; | |
| } | |
| /* Checks that the overriden functions (if any) have in contracts if | |
| * funcdecl has an in contract. | |
| */ | |
| void checkInContractOverrides() | |
| { | |
| if (funcdecl.frequires) | |
| { | |
| for (size_t i = 0; i < funcdecl.foverrides.dim; i++) | |
| { | |
| FuncDeclaration fdv = funcdecl.foverrides[i]; | |
| if (fdv.fbody && !fdv.frequires) | |
| { | |
| funcdecl.error("cannot have an in contract when overridden function `%s` does not have an in contract", fdv.toPrettyChars()); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| extern (C++) void semanticTypeInfoMembers(StructDeclaration sd) | |
| { | |
| if (sd.xeq && | |
| sd.xeq._scope && | |
| sd.xeq.semanticRun < PASS.semantic3done) | |
| { | |
| uint errors = global.startGagging(); | |
| sd.xeq.semantic3(sd.xeq._scope); | |
| if (global.endGagging(errors)) | |
| sd.xeq = sd.xerreq; | |
| } | |
| if (sd.xcmp && | |
| sd.xcmp._scope && | |
| sd.xcmp.semanticRun < PASS.semantic3done) | |
| { | |
| uint errors = global.startGagging(); | |
| sd.xcmp.semantic3(sd.xcmp._scope); | |
| if (global.endGagging(errors)) | |
| sd.xcmp = sd.xerrcmp; | |
| } | |
| FuncDeclaration ftostr = search_toString(sd); | |
| if (ftostr && | |
| ftostr._scope && | |
| ftostr.semanticRun < PASS.semantic3done) | |
| { | |
| ftostr.semantic3(ftostr._scope); | |
| } | |
| if (sd.xhash && | |
| sd.xhash._scope && | |
| sd.xhash.semanticRun < PASS.semantic3done) | |
| { | |
| sd.xhash.semantic3(sd.xhash._scope); | |
| } | |
| if (sd.postblit && | |
| sd.postblit._scope && | |
| sd.postblit.semanticRun < PASS.semantic3done) | |
| { | |
| sd.postblit.semantic3(sd.postblit._scope); | |
| } | |
| if (sd.dtor && | |
| sd.dtor._scope && | |
| sd.dtor.semanticRun < PASS.semantic3done) | |
| { | |
| sd.dtor.semantic3(sd.dtor._scope); | |
| } | |
| } |