From fff5dcf1cfbb298b6e4113fb35b2d698bb755118 Mon Sep 17 00:00:00 2001 From: k-hara Date: Fri, 5 Sep 2014 20:28:49 +0900 Subject: [PATCH] Add TemplateInstance::tnext to construct instantiation dependency graph To detect speculative instantiations in codegen phase, `tnext` is neccesary for the `test6()` case `runnable/link13350.d`. That's a big difference from the PR #2550. --- src/clone.c | 6 +- src/dsymbol.c | 2 +- src/expression.c | 3 +- src/scope.c | 14 ++- src/scope.h | 10 +- src/staticassert.c | 3 +- src/struct.c | 3 +- src/template.c | 191 +++++++++++++++----------------------- src/template.h | 8 +- src/traits.c | 3 +- test/runnable/link13350.d | 30 ++++++ 11 files changed, 138 insertions(+), 135 deletions(-) diff --git a/src/clone.c b/src/clone.c index 784ac3aa5549..154186d74a1f 100644 --- a/src/clone.c +++ b/src/clone.c @@ -93,7 +93,8 @@ FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc) unsigned errors = global.startGagging(); // Do not report errors, even if the sc = sc->push(); - sc->speculative = true; + sc->tinst = NULL; + sc->minst = NULL; for (size_t i = 0; i < 2; i++) { @@ -406,7 +407,8 @@ FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc) unsigned errors = global.startGagging(); // Do not report errors, even if the sc = sc->push(); - sc->speculative = true; + sc->tinst = NULL; + sc->minst = NULL; for (size_t j = 0; j < 2; j++) { diff --git a/src/dsymbol.c b/src/dsymbol.c index ab28c2333a43..5639ff8ca394 100644 --- a/src/dsymbol.c +++ b/src/dsymbol.c @@ -812,7 +812,7 @@ bool Dsymbol::inNonRoot() { if (ti->isTemplateMixin()) continue; - if (!ti->instantiatingModule || !ti->instantiatingModule->isRoot()) + if (!ti->minst || !ti->minst->isRoot()) return true; return false; } diff --git a/src/expression.c b/src/expression.c index 9b8b80b62f26..bfd64b8a14a7 100644 --- a/src/expression.c +++ b/src/expression.c @@ -5911,7 +5911,8 @@ Expression *IsExp::semantic(Scope *sc) Type *tded = NULL; Scope *sc2 = sc->copy(); // keep sc->flags - sc2->speculative = true; + sc2->tinst = NULL; + sc2->minst = NULL; Type *t = targ->trySemantic(loc, sc2); sc2->pop(); if (!t) diff --git a/src/scope.c b/src/scope.c index d62f5816ed30..901214a2daed 100644 --- a/src/scope.c +++ b/src/scope.c @@ -62,6 +62,7 @@ Scope::Scope() this->tf = NULL; this->os = NULL; this->tinst = NULL; + this->minst = NULL; this->sbreak = NULL; this->scontinue = NULL; this->fes = NULL; @@ -78,7 +79,6 @@ Scope::Scope() this->nofree = 0; this->noctor = 0; this->intypeof = 0; - this->speculative = false; this->lastVar = NULL; this->callSuper = 0; this->fieldinit = NULL; @@ -112,6 +112,10 @@ Scope *Scope::createGlobal(Module *module) sc->protection = Prot(PROTpublic); sc->module = module; + + sc->tinst = NULL; + sc->minst = module; + sc->scopesym = new ScopeDsymbol(); sc->scopesym->symtab = new DsymbolTable(); @@ -216,7 +220,8 @@ Scope *Scope::startCTFE() // If a template is instantiated from CT evaluated expression, // compiler can elide its code generation. - sc->speculative = true; + sc->tinst = NULL; + sc->minst = NULL; #endif return sc; } @@ -352,9 +357,8 @@ void Scope::mergeFieldInit(Loc loc, unsigned *fies) Module *Scope::instantiatingModule() { - if (tinst && tinst->instantiatingModule) - return tinst->instantiatingModule; - return module; + // TODO: in speculative context, returning 'module' is correct? + return minst ? minst : module; } Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym) diff --git a/src/scope.h b/src/scope.h index 629596b273fb..528e0b515a53 100644 --- a/src/scope.h +++ b/src/scope.h @@ -80,7 +80,6 @@ struct Scope SwitchStatement *sw; // enclosing switch statement TryFinallyStatement *tf; // enclosing try finally statement OnScopeStatement *os; // enclosing scope(xxx) statement - TemplateInstance *tinst; // enclosing template instance Statement *sbreak; // enclosing statement that supports "break" Statement *scontinue; // enclosing statement that supports "continue" ForeachStatement *fes; // if nested function for ForeachStatement, this is it @@ -89,9 +88,16 @@ struct Scope int nofree; // set if shouldn't free it int noctor; // set if constructor calls aren't allowed int intypeof; // in typeof(exp) - bool speculative; // in __traits(compiles) and so on VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init + /* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope). + * If !minst && !tinst, it's in definitely speculative scope (eg. template constraint). + * If minst && tinst, it's in instantiated code scope without speculation. + * If !minst && tinst, it's in instantiated code scope with speculation. + */ + Module *minst; // root module where the instantiated templates should belong to + TemplateInstance *tinst; // enclosing template instance + unsigned callSuper; // primitive flow analysis for constructors unsigned *fieldinit; size_t fieldinit_dim; diff --git a/src/staticassert.c b/src/staticassert.c index 0e7d2e6b704e..d0ea66f6198b 100644 --- a/src/staticassert.c +++ b/src/staticassert.c @@ -52,7 +52,8 @@ void StaticAssert::semantic2(Scope *sc) //printf("StaticAssert::semantic2() %s\n", toChars()); ScopeDsymbol *sds = new ScopeDsymbol(); sc = sc->push(sds); - sc->speculative = true; + sc->tinst = NULL; + sc->minst = NULL; sc->flags |= SCOPEcondition; sc = sc->startCTFE(); diff --git a/src/struct.c b/src/struct.c index eb72ee451bb2..354b8985ea44 100644 --- a/src/struct.c +++ b/src/struct.c @@ -859,7 +859,8 @@ void StructDeclaration::semantic(Scope *sc) { unsigned xerrors = global.startGagging(); sc = sc->push(); - sc->speculative = true; + sc->tinst = NULL; + sc->minst = NULL; FuncDeclaration *fcall = resolveFuncCall(loc, sc, scall, NULL, NULL, NULL, 1); sc = sc->pop(); global.endGagging(xerrors); diff --git a/src/template.c b/src/template.c index f067fedd1613..89df1efe8a18 100644 --- a/src/template.c +++ b/src/template.c @@ -730,8 +730,8 @@ bool TemplateDeclaration::evaluateConstraint( Scope *scx = paramscope->push(ti); scx->parent = ti; - scx->tinst = ti; - scx->speculative = true; + scx->tinst = NULL; + scx->minst = NULL; assert(!ti->symtab); if (fd) @@ -868,6 +868,7 @@ MATCH TemplateDeclaration::matchWithInstance(Scope *sc, TemplateInstance *ti, paramsym->parent = scope->parent; Scope *paramscope = scope->push(paramsym); paramscope->tinst = ti; + paramscope->minst = sc->minst; paramscope->callsc = sc; paramscope->stc = 0; @@ -1026,11 +1027,6 @@ MATCH TemplateDeclaration::leastAsSpecialized(Scope *sc, TemplateDeclaration *td */ TemplateInstance ti(Loc(), ident); // create dummy template instance - ti.tinst = this->getInstantiating(sc); - if (ti.tinst) - ti.instantiatingModule = ti.tinst->instantiatingModule; - else - ti.instantiatingModule = sc->instantiatingModule(); // Set type arguments to dummy template instance to be types // generated from the parameters to this template declaration ti.tiargs = new Objects(); @@ -1168,6 +1164,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( paramsym->parent = scope->parent; // should use hasnestedArgs and enclosing? Scope *paramscope = scope->push(paramsym); paramscope->tinst = ti; + paramscope->minst = sc->minst; paramscope->callsc = sc; paramscope->stc = 0; @@ -1852,17 +1849,17 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( // Partially instantiate function for constraint and fd->leastAsSpecialized() { assert(paramsym); - Scope *sc1 = scope->push(paramsym); - sc1->tinst = ti; - - Scope *sc2 = sc1->push(ti); + Scope *sc2 = scope; + sc2 = sc2->push(paramsym); + sc2 = sc2->push(ti); sc2->parent = ti; sc2->tinst = ti; + sc2->minst = sc->minst; fd = doHeaderInstantiation(ti, sc2, fd, tthis, fargs); - sc2->pop(); - sc1->pop(); + sc2 = sc2->pop(); + sc2 = sc2->pop(); if (!fd) goto Lnomatch; @@ -2199,12 +2196,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, if (!tiargs) tiargs = new Objects(); TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); - ti->tinst = td->getInstantiating(sc); - if (ti->tinst) - ti->instantiatingModule = ti->tinst->instantiatingModule; - else - ti->instantiatingModule = sc->instantiatingModule(); - Objects dedtypes; dedtypes.setDim(td->parameters->dim); assert(td->semanticRun != PASSinit); @@ -2315,11 +2306,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, /* This is a 'dummy' instance to evaluate constraint properly. */ TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); - ti->tinst = td->getInstantiating(sc); - if (ti->tinst) - ti->instantiatingModule = ti->tinst->instantiatingModule; - else - ti->instantiatingModule = sc->instantiatingModule(); ti->parent = td->parent; // Maybe calculating valid 'enclosing' is unnecessary. FuncDeclaration *fd = f; @@ -2775,21 +2761,6 @@ void TemplateDeclaration::removeInstance(TemplateInstance *handle) --numinstances; } -/******************************************* - * Returns template instance which instantiating this template declaration. - */ - -TemplateInstance *TemplateDeclaration::getInstantiating(Scope *sc) -{ - /* If this is instantiated declaration in root module, Return it. - */ - TemplateInstance *tinst = isInstantiated(); - if (tinst && (!tinst->instantiatingModule || tinst->instantiatingModule->isRoot())) - return tinst; - - return sc->tinst; -} - /* ======================== Type ============================================ */ /**** @@ -5661,9 +5632,10 @@ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) this->name = ident; this->tiargs = NULL; this->tempdecl = NULL; - this->instantiatingModule = NULL; this->inst = NULL; this->tinst = NULL; + this->tnext = NULL; + this->minst = NULL; this->deferred = NULL; this->argsym = NULL; this->aliasdecl = NULL; @@ -5672,7 +5644,6 @@ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) this->havetempdecl = false; this->enclosing = NULL; this->gagged = false; - this->speculative = false; this->hash = 0; this->fargs = NULL; } @@ -5692,9 +5663,10 @@ TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *ti this->name = td->ident; this->tiargs = tiargs; this->tempdecl = td; - this->instantiatingModule = NULL; this->inst = NULL; this->tinst = NULL; + this->tnext = NULL; + this->minst = NULL; this->deferred = NULL; this->argsym = NULL; this->aliasdecl = NULL; @@ -5703,7 +5675,6 @@ TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *ti this->havetempdecl = true; this->enclosing = NULL; this->gagged = false; - this->speculative = false; this->hash = 0; this->fargs = NULL; @@ -5843,28 +5814,13 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) return; } - Module *mi = sc->instantiatingModule(); - if (!instantiatingModule || instantiatingModule->isRoot()) - instantiatingModule = mi; - //printf("mi = %s\n", mi->toChars()); - - /* Get the enclosing template instance from the scope tinst - */ + // Get the enclosing template instance from the scope tinst tinst = sc->tinst; - if (global.gag) - gagged = true; - if (sc->speculative || (tinst && tinst->speculative)) - { - //printf("\tspeculative ti %s '%s' gag = %d, spec = %d\n", tempdecl->parent->toChars(), toChars(), global.gag, sc->speculative); - speculative = true; - } - if (sc->flags & (SCOPEconstraint | SCOPEcompile)) - { - // Disconnect the chain if this instantiation is in definitely speculative context. - // It should be done after sc->instantiatingModule(). - tinst = NULL; - } + // Get the instantiating module from the scope minst + minst = sc->minst; + + gagged = (global.gag > 0); semanticRun = PASSsemantic; @@ -5919,46 +5875,37 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) // If both this and the previous instantiation were gagged, // use the number of errors that happened last time. - if (inst->gagged && gagged) - { - global.errors += inst->errors; - global.gaggedErrors += inst->errors; - } + global.errors += errors; + global.gaggedErrors += errors; // If the first instantiation was gagged, but this is not: - if (inst->gagged && !gagged) + if (inst->gagged) { // It had succeeded, mark it is a non-gagged instantiation, // and reuse it. - inst->gagged = false; + inst->gagged = gagged; } - // If the first instantiation was speculative, but this is not: - if (inst->speculative && !sc->speculative) - { - // Mark it is a non-speculative instantiation. - inst->speculative = false; - - // Bugzilla 13400: When an instance is changed to non-speculative, - // its instantiatingModule should also be updated. - // See test/runnable/link13400.d - inst->instantiatingModule = mi; - } + this->tnext = inst->tnext; + inst->tnext = this; // If the first instantiation was in speculative context, but this is not: - if (!inst->tinst && inst->speculative && - tinst && !(sc->flags & (SCOPEconstraint | SCOPEcompile))) + if (tinst && !inst->tinst && !inst->minst) { // Reconnect the chain if this instantiation is not in speculative context. - TemplateInstance *tix = tinst; - while (tix && tix != inst) - tix = tix->tinst; - if (tix != inst) // Bugzilla 13379: Prevent circular chain + TemplateInstance *ti = tinst; + while (ti && ti != inst) + ti = ti->tinst; + if (ti != inst) // Bugzilla 13379: Prevent circular chain inst->tinst = tinst; } - if (!inst->instantiatingModule || inst->instantiatingModule->isRoot()) - inst->instantiatingModule = mi; + // If the first instantiation was speculative, but this is not: + if (!inst->minst) + { + // Mark it is a non-speculative instantiation. + inst->minst = minst; + } #if LOG printf("\tit's a match with instance %p, %d\n", inst, inst->semanticRun); @@ -5987,7 +5934,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) Dsymbols *target_symbol_list; size_t target_symbol_list_idx = 0; //if (sc->scopesym) printf("3: sc is %s %s\n", sc->scopesym->kind(), sc->scopesym->toChars()); - if (sc->scopesym && sc->scopesym->members && !sc->scopesym->isTemplateMixin()) + if (!tinst && sc->scopesym && sc->scopesym->members) { /* A module can have explicit template instance and its alias * in module scope (e,g, `alias Base64Impl!('+', '/') Base64;`). @@ -6071,6 +6018,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) argsym->parent = scope->parent; scope = scope->push(argsym); scope->tinst = this; + scope->minst = minst; //scope->stc = 0; // Declare each template parameter as an alias for the argument type @@ -6138,7 +6086,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) //printf("enclosing = %d, sc->parent = %s\n", enclosing, sc->parent->toChars()); sc2->parent = this; sc2->tinst = this; - sc2->speculative = speculative; + sc2->minst = minst; tryExpandMembers(sc2); @@ -7080,12 +7028,6 @@ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) if (!flag) { - ti->tinst = td->getInstantiating(sc); - if (ti->tinst) - ti->instantiatingModule = ti->tinst->instantiatingModule; - else - ti->instantiatingModule = sc->instantiatingModule(); - /* Calculate the need for overload resolution. * When only one template can match with tiargs, inference is not necessary. */ @@ -7428,6 +7370,7 @@ void TemplateInstance::semantic2(Scope *sc) sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; + sc->minst = minst; int needGagging = (gagged && !global.gag); unsigned int olderrors = global.errors; @@ -7486,6 +7429,7 @@ void TemplateInstance::semantic3(Scope *sc) sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; + sc->minst = minst; int needGagging = (gagged && !global.gag); unsigned int olderrors = global.errors; @@ -7774,11 +7718,11 @@ void unSpeculative(Scope *sc, RootObject *o) { // If the instance is already non-speculative, // or it is leaked to the speculative scope. - if (!ti->speculative || sc->speculative) + if (ti->minst != NULL || sc->minst == NULL) return; // Remark as non-speculative instance. - ti->speculative = false; + ti->minst = sc->minst; if (!ti->tinst) ti->tinst = sc->tinst; @@ -7793,7 +7737,7 @@ void unSpeculative(Scope *sc, RootObject *o) * Returns true if this is not instantiated in non-root module, and * is a part of non-speculative instantiatiation. * - * Note: instantiatingModule does not stabilize until semantic analysis is completed, + * Note: minst does not stabilize until semantic analysis is completed, * so don't call this function during semantic analysis to return precise result. */ bool TemplateInstance::needsCodegen() @@ -7808,8 +7752,8 @@ bool TemplateInstance::needsCodegen() global.params.allInst || global.params.debuglevel) { - //printf("%s instantiatingModule = %s, speculative = %d, enclosing in nonRoot = %d\n", - // toPrettyChars(), instantiatingModule ? instantiatingModule->toChars() : NULL, speculative, + //printf("%s minst = %s, enclosing in nonRoot = %d\n", + // toPrettyChars(), minst ? minst->toChars() : NULL, // enclosing && !enclosing->isInstantiated() && enclosing->inNonRoot()); if (enclosing) { @@ -7823,15 +7767,38 @@ bool TemplateInstance::needsCodegen() return true; } - if (instantiatingModule && !instantiatingModule->isRoot()) + // If this may be a speculative instantiation: + if (!minst) { - Module *mi = instantiatingModule; + for (TemplateInstance *ti = this; ti; ti = ti->tnext) + { + TemplateInstance *tix = ti; + while (tix && !tix->minst) + tix = tix->tinst; + if (tix) + { + assert(tix->minst); + // cache the result, ti is in non-speculative instantiation chain + minst = tix->minst; + return tix->needsCodegen(); + } + // ti was speculative. + } + + // cache the result, mark as definitely speculative + tinst = NULL; + minst = NULL; + return false; + } + + if (!minst->isRoot()) + { /* If a TemplateInstance is ever instantiated by non-root modules, * we do not have to generate code for it, * because it will be generated when the non-root module is compiled. * - * But, if mi imports any root modules, we still need to generate the code. + * But, if minst imports any root modules, we still need to generate the code. * * The problem is if A imports B, and B imports A, and both A * and B instantiate the same template, does the compilation of A @@ -7840,21 +7807,13 @@ bool TemplateInstance::needsCodegen() * See bugzilla 2500. */ - if (!mi->rootImports()) + if (!minst->rootImports()) { - //printf("instantiated by %s %s\n", instantiatingModule->toChars(), toChars()); + //printf("instantiated by %s %s\n", minst->toChars(), toChars()); return false; } } - - for (TemplateInstance *ti = this; ti; ti = ti->tinst) - { - //printf("\tti = %s spec = %d\n", ti->toChars(), ti->speculative); - if (!ti->speculative) - return true; - } - - return false; + return true; } /* ======================== TemplateMixin ================================ */ diff --git a/src/template.h b/src/template.h index cd06e86b320b..69b3ff28d77e 100644 --- a/src/template.h +++ b/src/template.h @@ -105,8 +105,6 @@ class TemplateDeclaration : public ScopeDsymbol TemplateInstance *addInstance(TemplateInstance *ti); void removeInstance(TemplateInstance *handle); - TemplateInstance *getInstantiating(Scope *sc); - TemplateDeclaration *isTemplateDeclaration() { return this; } TemplateTupleParameter *isVariadic(); @@ -317,7 +315,6 @@ class TemplateInstance : public ScopeDsymbol Dsymbol *enclosing; // if referencing local symbols, this is the context Dsymbol *aliasdecl; // !=NULL if instance is an alias for its sole member TemplateInstance *inst; // refer to existing instance - TemplateInstance *tinst; // enclosing template instance ScopeDsymbol *argsym; // argument symbol table int nest; // for recursion detection bool semantictiargsdone; // has semanticTiargs() been done? @@ -330,8 +327,9 @@ class TemplateInstance : public ScopeDsymbol // Used to determine the instance needs code generation. // Note that these are inaccurate until semantic analysis phase completed. - Module *instantiatingModule; // the top module that instantiated this instance - bool speculative; // if the instantiation is speculative + TemplateInstance *tinst; // enclosing template instance + TemplateInstance *tnext; // non-first instantiated instances + Module *minst; // the top module that instantiated this instance TemplateInstance(Loc loc, Identifier *temp_id); TemplateInstance(Loc loc, TemplateDeclaration *tempdecl, Objects *tiargs); diff --git a/src/traits.c b/src/traits.c index fe1bdbef272e..4d3aa29ca29e 100644 --- a/src/traits.c +++ b/src/traits.c @@ -908,7 +908,8 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) { unsigned errors = global.startGagging(); Scope *sc2 = sc->push(); - sc2->speculative = true; + sc2->tinst = NULL; + sc2->minst = NULL; sc2->flags = (sc->flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile; bool err = false; diff --git a/test/runnable/link13350.d b/test/runnable/link13350.d index b9f7621f665d..bceb4b8586bb 100644 --- a/test/runnable/link13350.d +++ b/test/runnable/link13350.d @@ -95,6 +95,35 @@ void test5() /**********************************/ +int foo6()() { return 0; } + +template A6() { alias f = foo6!(); } +void testa6()() if (is(typeof(A6!().f))) {} + +template B6() { alias f = foo6!(); } +void testb6()() if (is(typeof(B6!().f))) {} + +template C6() { void f() { B6!().f(); } } + +void test6() +{ + testa6(); + // foo6!() is speculatively instantiated from A6!() [TemplateInstance a] + // -> foo6!() is instantiated in A6!(), so it should be inserted to the members of this module. + + testb6(); + // foo6!() is speculatively instantiated from B6!() [TemplateInstance b], + // but the tinst of cached [TemplateInstance a] is not changed. + // -> insert [b] to the tnext chain of [a] + + C6!().f(); + // foo6!() is used through C6!(), so it should be linked to the final executable. + // but its first instance does not link to any non-speculative instances. + // -> look for tnext chain and determine its codegen is really necessary. +} + +/**********************************/ + int main() { test13350(); @@ -103,6 +132,7 @@ int main() test3(); test4(); test5(); + test6(); printf("Success\n"); return 0;