From 6c14fadca403a61a578795ec0fb71f9340c7b543 Mon Sep 17 00:00:00 2001 From: k-hara Date: Sun, 21 Jun 2015 23:54:07 +0900 Subject: [PATCH 1/8] fix Issue 14431 - huge slowdown of compilation speed In the pull request #4384, all instance has been changed to invoke semantic3(). It was for the link-failure issue in specific case, but it was too excessive. 1. Semantic analysis strategy for template instances: We cannot determine which instance does not need to be placed in object file until semantic analysis completed. Therefore, for all templates instantiated in root module, compiler should invoke their semantic3 -- regardless of whether those are also instantiated in non-root module. If a template is _only_ instantiated in non-root module, we can elide its semantic3 (and for the compilation speed we should do that). 2. Code generation strategy for template instances: If a template is instantiated in non-root module, compiler usually does not have to put it in object file. But if a template is instantiated in both of root and non-root modules which mutually import each other, it needs to placed in objfile. --- src/struct.c | 8 +- src/template.c | 180 +++++++++++++++----------- src/template.h | 1 + test/runnable/extra-files/test14198.d | 5 +- test/runnable/link14198b.sh | 6 +- 5 files changed, 121 insertions(+), 79 deletions(-) diff --git a/src/struct.c b/src/struct.c index ec186e52ff17..3e8cbe8fd882 100644 --- a/src/struct.c +++ b/src/struct.c @@ -100,8 +100,12 @@ void semanticTypeInfo(Scope *sc, Type *t) // If the struct is in a non-root module, run semantic3 to get // correct symbols for the member function. - // Note that, all instantiated symbols will run semantic3. - if (sd->inNonRoot()) + if (TemplateInstance *ti = sd->isInstantiated()) + { + if (ti->minst && !ti->minst->isRoot()) + Module::addDeferredSemantic3(sd); + } + else if (sd->inNonRoot()) { //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot()); Module::addDeferredSemantic3(sd); diff --git a/src/template.c b/src/template.c index 62535bd59b7a..72a9e2fcd72b 100644 --- a/src/template.c +++ b/src/template.c @@ -5942,22 +5942,35 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) this->tnext = inst->tnext; inst->tnext = this; - // If the first instantiation was in speculative context, but this is not: - if (tinst && !inst->tinst && !inst->minst) + if (minst && minst->isRoot() && !(inst->minst && inst->minst->isRoot())) { - // Reconnect the chain if this instantiation is not in speculative context. + /* Swap the position of 'inst' and 'this' in the instantiation graph. + * Then, the primary instance `inst` will be changed to a root instance. + * + * Before: + * non-root -> A!() -> B!()[inst] -> C!() + * | + * root -> D!() -> B!()[this] + * + * After: + * non-root -> A!() -> B!()[this] + * | + * root -> D!() -> B!()[inst] -> C!() + */ + Module *mi = minst; TemplateInstance *ti = tinst; - while (ti && ti != inst) - ti = ti->tinst; - if (ti != inst) // Bugzilla 13379: Prevent circular chain - inst->tinst = tinst; - } + minst = inst->minst; + tinst = inst->tinst; + inst->minst = mi; + inst->tinst = ti; - // If the first instantiation was speculative, but this is not: - if (!inst->minst) - { - // Mark it is a non-speculative instantiation. - inst->minst = minst; + if (minst) // if inst was not speculative + { + /* Add 'inst' once again to the root module members[], then the + * instance members will get codegen chances. + */ + inst->appendToModuleMember(); + } } #if LOG @@ -5979,66 +5992,10 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) //getIdent(); - // Add 'this' to the enclosing scope's members[] so the semantic routines - // will get called on the instance members. Store the place we added it to - // in target_symbol_list(_idx) so we can remove it later if we encounter - // an error. -#if 1 - 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 (0 && !tinst && sc->scopesym && sc->scopesym->members) - { - /* A module can have explicit template instance and its alias - * in module scope (e,g, `alias Base64Impl!('+', '/') Base64;`). - * When the module is just imported, normally compiler can assume that - * its instantiated code would be contained in the separately compiled - * obj/lib file (e.g. phobos.lib). - * Bugzilla 2644: However, if the template is instantiated in both - * modules of root and non-root, compiler should generate its objcode. - * Therefore, always conservatively insert this instance to the member of - * a root module, then calculate the necessity by TemplateInstance::needsCodegen(). - */ - //if (sc->scopesym->isModule()) - // printf("module level instance %s\n", toChars()); - - //printf("\t1: adding to %s %s\n", sc->scopesym->kind(), sc->scopesym->toChars()); - target_symbol_list = sc->scopesym->members; - } - else - { - Dsymbol *s = enclosing ? enclosing : tempdecl->parent; - while (s && !s->isModule()) - s = s->toParent2(); - assert(s); - Module *m = (Module *)s; - if (!m->isRoot()) - m = m->importedFrom; - - //printf("\t2: adding to module %s instead of module %s\n", m->toChars(), sc->module->toChars()); - target_symbol_list = m->members; - - /* Defer semantic3 running in order to avoid mutual forward reference. - * See test/runnable/test10736.d - */ - if (m->semanticRun >= PASSsemantic3done) - Module::addDeferredSemantic3(this); - } - for (size_t i = 0; 1; i++) - { - if (i == target_symbol_list->dim) - { - target_symbol_list_idx = i; - target_symbol_list->push(this); - break; - } - if (this == (*target_symbol_list)[i]) // if already in Array - { - target_symbol_list = NULL; - break; - } - } -#endif + // Store the place we added it to in target_symbol_list(_idx) so we can + // remove it later if we encounter an error. + Dsymbols *target_symbol_list = appendToModuleMember(); + size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list->dim - 1 : 0; // Copy the syntax trees from the TemplateDeclaration members = Dsymbol::arraySyntaxCopy(tempdecl->members); @@ -7288,6 +7245,83 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) return nested != 0; } +/***************************************** + * Append 'this' to the enclosing module members[] so the semantic routines + * will get called on the instance members. + */ +Dsymbols *TemplateInstance::appendToModuleMember() +{ + Module *md = tempdecl->getModule(); + + Module *mi = minst; + if (mi) + { + if (mi->isRoot()) + { + /* If both modules mi and md where the template is declared are + * roots, instead use md to store the generated code in it. + */ + if (md && md->isRoot()) + mi = md; + } + else + { + /* A module can have explicit template instance and its alias + * in module scope (e,g, `alias Base64Impl!('+', '/') Base64;`). + * When the module is just imported, normally compiler can assume that + * its instantiated code would be contained in the separately compiled + * obj/lib file (e.g. phobos.lib). + */ + /* + * Bugzilla 2644: However, if the template is instantiated in both + * modules of root and non-root, compiler should generate its objcode. + * Therefore, always conservatively insert this instance to the member of + * a root module, then calculate the necessity by TemplateInstance::needsCodegen(). + */ + } + //printf("\t1: adding to %s\n", mi->toPrettyChars()); + } + else + { + // 'this' is speculative instance + if (md && md->isRoot()) + { + mi = md; + } + else + { + // select arbitrary root module + Dsymbol *s = enclosing ? enclosing : tempdecl->parent; + while (s && !s->isModule()) + s = s->toParent2(); + assert(s); + mi = (Module *)s; + if (!mi->isRoot()) + mi = mi->importedFrom; + } + assert(mi->isRoot()); + //printf("\t2: adding to module %s instead of module %s\n", mi->toPrettyChars(), sc->module->toPrettyChars()); + } + + Dsymbols *a = mi->members; + for (size_t i = 0; 1; i++) + { + if (i == a->dim) + { + a->push(this); + if (mi->semanticRun >= PASSsemantic3done && mi->isRoot()) + Module::addDeferredSemantic3(this); + break; + } + if (this == (*a)[i]) // if already in Array + { + a = NULL; + break; + } + } + return a; +} + /**************************************** * This instance needs an identifier for name mangling purposes. * Create one by taking the template declaration name and adding diff --git a/src/template.h b/src/template.h index 2510c31d28b7..b6c8f82847ec 100644 --- a/src/template.h +++ b/src/template.h @@ -351,6 +351,7 @@ class TemplateInstance : public ScopeDsymbol bool findBestMatch(Scope *sc, Expressions *fargs); bool needsTypeInference(Scope *sc, int flag = 0); bool hasNestedArgs(Objects *tiargs, bool isstatic); + Dsymbols *appendToModuleMember(); void declareParameters(Scope *sc); Identifier *genIdent(Objects *args); void expandMembers(Scope *sc); diff --git a/test/runnable/extra-files/test14198.d b/test/runnable/extra-files/test14198.d index b498ea65f535..46dd2c585a38 100644 --- a/test/runnable/extra-files/test14198.d +++ b/test/runnable/extra-files/test14198.d @@ -13,8 +13,11 @@ struct S to!string(false); // [1] to!string(bool src) should be deduced to pure @safe, and the function will be mangled to: // --> _D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya - // [2] its object code should be stored in the library file, because it's instantiated in std14188.uni: + // [2] its object code would be stored in the library file, because it's instantiated in std14188.uni: // --> FormatSpec!char --> to!string(bool src) in FormatSpec!char.toString() + // But semanti3 of FormatSpec!char.toString() won't get called from this module compilation, + // so the instantiaion is invisible. + // Then, the object code is also stored in test14198.obj, and the link will succeed. } else static assert(0); diff --git a/test/runnable/link14198b.sh b/test/runnable/link14198b.sh index 07e6814d0c8b..038c0e245001 100755 --- a/test/runnable/link14198b.sh +++ b/test/runnable/link14198b.sh @@ -13,13 +13,13 @@ else fi libname=${dir}${SEP}lib14198b${LIBEXT} -# Do link failure without library file. +# Do not link failure even without library file. $DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} ${src}${SEP}test14198.d > ${output_file} 2>&1 -grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1 +grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} && exit 1 $DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} -version=bug14198 ${src}${SEP}test14198.d > ${output_file} 2>&1 -grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1 +grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} && exit 1 rm ${dir}/{test14198b${OBJ},test14198b${EXE}} From 5b464f3393980a0229c079ff6e646ef843681996 Mon Sep 17 00:00:00 2001 From: k-hara Date: Thu, 13 Aug 2015 19:05:22 +0900 Subject: [PATCH 2/8] fix Issue 14901 - template static shared this() run multiple times with separate compilation --- test/runnable/imports/test14901a.d | 21 +++++++++++++++++++++ test/runnable/imports/test14901b.d | 13 +++++++++++++ test/runnable/imports/test14901c.d | 10 ++++++++++ test/runnable/imports/test14901d.d | 8 ++++++++ test/runnable/test14901.d | 20 ++++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 test/runnable/imports/test14901a.d create mode 100644 test/runnable/imports/test14901b.d create mode 100644 test/runnable/imports/test14901c.d create mode 100644 test/runnable/imports/test14901d.d create mode 100644 test/runnable/test14901.d diff --git a/test/runnable/imports/test14901a.d b/test/runnable/imports/test14901a.d new file mode 100644 index 000000000000..29ac9dc8eac2 --- /dev/null +++ b/test/runnable/imports/test14901a.d @@ -0,0 +1,21 @@ +module imports.test14901a; + +//extern(C) int printf(const char*, ...); + +extern extern(C) __gshared static int initCount; + +int make(string s)() +{ + __gshared static int value; + + struct WithCtor + { + shared static this() + { + //printf("%s\n", s.ptr); + initCount++; + } + } + + return value; +} diff --git a/test/runnable/imports/test14901b.d b/test/runnable/imports/test14901b.d new file mode 100644 index 000000000000..995f5e6a05c4 --- /dev/null +++ b/test/runnable/imports/test14901b.d @@ -0,0 +1,13 @@ +module imports.test14901b; + +import imports.test14901a; + +alias bar = make!"bar"; + +struct User(int id) +{ + int foo() + { + return bar; + } +} diff --git a/test/runnable/imports/test14901c.d b/test/runnable/imports/test14901c.d new file mode 100644 index 000000000000..db36fc77f6ff --- /dev/null +++ b/test/runnable/imports/test14901c.d @@ -0,0 +1,10 @@ +module imports.test14901c; + +import imports.test14901b; + +shared static this() {} + +void caller1() +{ + User!1 u; +} diff --git a/test/runnable/imports/test14901d.d b/test/runnable/imports/test14901d.d new file mode 100644 index 000000000000..541de9d9b6f4 --- /dev/null +++ b/test/runnable/imports/test14901d.d @@ -0,0 +1,8 @@ +module imports.test14901d; + +import imports.test14901b; + +void caller2() +{ + User!2 u; +} diff --git a/test/runnable/test14901.d b/test/runnable/test14901.d new file mode 100644 index 000000000000..73a357a40866 --- /dev/null +++ b/test/runnable/test14901.d @@ -0,0 +1,20 @@ +// REQUIRED_ARGS: +// PERMUTE_ARGS: -unittest +// EXTRA_SOURCES: imports/test14901a.d imports/test14901b.d imports/test14901c.d imports/test14901d.d +// COMPILE_SEPARATELY + +module test14901; + +import imports.test14901c; +import imports.test14901d; + +extern(C) __gshared static int initCount; + +extern(C) int printf(const char*, ...); + +void main() +{ + caller1(); + caller2(); + assert(initCount == 1); +} From 7cced26789de95e2e14c1452c7334d50f68f9deb Mon Sep 17 00:00:00 2001 From: k-hara Date: Fri, 28 Aug 2015 17:31:41 +0900 Subject: [PATCH 3/8] (will be squashed later) Better appendToModuleMember implementation and update comments --- src/template.c | 78 ++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/src/template.c b/src/template.c index 72a9e2fcd72b..aa76685b0bd8 100644 --- a/src/template.c +++ b/src/template.c @@ -5942,6 +5942,17 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) this->tnext = inst->tnext; inst->tnext = this; + /* A module can have explicit template instance and its alias + * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`). + * If the first instantiation 'inst' had happened in non-root module, + * compiler can assume that its instantiated code would be included + * in the separately compiled obj/lib file (e.g. phobos.lib). + * + * However, if 'this' second instantiation happened in root module, + * compiler might need to invoke its codegen (Bugzilla 2500 & 2644). + * But whole import graph is not determined until all semantic pass finished, + * so 'inst' should conservatively finish the semantic3 pass for the codegen. + */ if (minst && minst->isRoot() && !(inst->minst && inst->minst->isRoot())) { /* Swap the position of 'inst' and 'this' in the instantiation graph. @@ -7246,61 +7257,34 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) } /***************************************** - * Append 'this' to the enclosing module members[] so the semantic routines - * will get called on the instance members. + * Append 'this' to the specific module members[] */ Dsymbols *TemplateInstance::appendToModuleMember() { - Module *md = tempdecl->getModule(); + Module *mi = minst; // instantiated -> inserted module - Module *mi = minst; - if (mi) + if (!mi || mi->isRoot()) { - if (mi->isRoot()) - { - /* If both modules mi and md where the template is declared are - * roots, instead use md to store the generated code in it. - */ - if (md && md->isRoot()) - mi = md; - } - else - { - /* A module can have explicit template instance and its alias - * in module scope (e,g, `alias Base64Impl!('+', '/') Base64;`). - * When the module is just imported, normally compiler can assume that - * its instantiated code would be contained in the separately compiled - * obj/lib file (e.g. phobos.lib). - */ - /* - * Bugzilla 2644: However, if the template is instantiated in both - * modules of root and non-root, compiler should generate its objcode. - * Therefore, always conservatively insert this instance to the member of - * a root module, then calculate the necessity by TemplateInstance::needsCodegen(). - */ - } - //printf("\t1: adding to %s\n", mi->toPrettyChars()); + /* If the instantiated module is speculative or root, insert to the + * member of a root module. Then: + * - semantic3 pass will get called on the instance members. + * - codegen pass will get a selection chance to do/skip it. + */ + + // insert target is made stable by using the module + // where tempdecl is declared. + mi = tempdecl->getModule(); + if (!mi->isRoot()) + mi = mi->importedFrom; + assert(mi->isRoot()); } else { - // 'this' is speculative instance - if (md && md->isRoot()) - { - mi = md; - } - else - { - // select arbitrary root module - Dsymbol *s = enclosing ? enclosing : tempdecl->parent; - while (s && !s->isModule()) - s = s->toParent2(); - assert(s); - mi = (Module *)s; - if (!mi->isRoot()) - mi = mi->importedFrom; - } - assert(mi->isRoot()); - //printf("\t2: adding to module %s instead of module %s\n", mi->toPrettyChars(), sc->module->toPrettyChars()); + /* If the instantiated module is non-root, insert to the member of the + * non-root module. Then: + * - semantic3 pass won't be called on the instance. + * - codegen pass won't reach to the instance. + */ } Dsymbols *a = mi->members; From a587c3d6a49c198822a0629071c401e62d592787 Mon Sep 17 00:00:00 2001 From: k-hara Date: Thu, 27 Aug 2015 15:26:28 +0900 Subject: [PATCH 4/8] Prefer instantiations from root modules when -debug/-unittest is specified --- src/template.c | 88 ++++++++++++++----- test/runnable/extra-files/linkdebug.d | 16 ++++ .../extra-files/linkdebug_primitives.d | 13 +++ test/runnable/extra-files/linkdebug_range.d | 48 ++++++++++ test/runnable/extra-files/linkdebug_uni.d | 19 ++++ test/runnable/linkdebug.sh | 21 +++++ 6 files changed, 182 insertions(+), 23 deletions(-) create mode 100644 test/runnable/extra-files/linkdebug.d create mode 100644 test/runnable/extra-files/linkdebug_primitives.d create mode 100644 test/runnable/extra-files/linkdebug_range.d create mode 100644 test/runnable/extra-files/linkdebug_uni.d create mode 100755 test/runnable/linkdebug.sh diff --git a/src/template.c b/src/template.c index aa76685b0bd8..fcc128ca2179 100644 --- a/src/template.c +++ b/src/template.c @@ -7263,6 +7263,14 @@ Dsymbols *TemplateInstance::appendToModuleMember() { Module *mi = minst; // instantiated -> inserted module + if (global.params.useUnitTests || + global.params.debuglevel) + { + // Turn all non-root instances to speculative + if (mi && !mi->isRoot()) + mi = NULL; + } + if (!mi || mi->isRoot()) { /* If the instantiated module is speculative or root, insert to the @@ -7855,15 +7863,8 @@ void unSpeculative(Scope *sc, RootObject *o) */ bool TemplateInstance::needsCodegen() { - /* The issue is that if the importee is compiled with a different -debug - * setting than the importer, the importer may believe it exists - * in the compiled importee when it does not, when the instantiation - * is behind a conditional debug declaration. - */ - // workaround for Bugzilla 11239 - if (global.params.useUnitTests || - global.params.allInst || - global.params.debuglevel) + // Now -allInst is just for the backward compatibility. + if (global.params.allInst) { //printf("%s minst = %s, enclosing (%s)->isNonRoot = %d\n", // toPrettyChars(), minst ? minst->toChars() : NULL, @@ -7894,9 +7895,12 @@ bool TemplateInstance::needsCodegen() return true; } - // If this may be a speculative instantiation: if (!minst) { + // If this is a speculative instantiation, + // 1. do codegen if ancestors really needs codegen. + // 2. become non-speculative if siblings are not speculative + TemplateInstance *tnext = this->tnext; TemplateInstance *tinst = this->tinst; // At first, disconnect chain first to prevent infinite recursion. @@ -7911,51 +7915,89 @@ bool TemplateInstance::needsCodegen() assert(minst->isRoot() || minst->rootImports()); return true; } - if (tnext && tnext->needsCodegen()) + if (tnext && (tnext->needsCodegen() || tnext->minst)) { minst = tnext->minst; // cache result assert(minst); - assert(minst->isRoot() || minst->rootImports()); - return true; + return minst->isRoot() || minst->rootImports(); } + + // Elide codegen because this is really speculative. return false; } - if (minst->isRoot()) + /* The issue is that if the importee is compiled with a different -debug + * setting than the importer, the importer may believe it exists + * in the compiled importee when it does not, when the instantiation + * is behind a conditional debug declaration. + */ + // workaround for Bugzilla 11239 + if (global.params.useUnitTests || + global.params.debuglevel) { - // Prefer instantiation in non-root module, to minimize object code size + // Prefer instantiations from root modules, to maximize link-ability. + if (minst->isRoot()) + return true; + TemplateInstance *tnext = this->tnext; + TemplateInstance *tinst = this->tinst; this->tnext = NULL; + this->tinst = NULL; - if (tnext && !tnext->needsCodegen() && tnext->minst) + if (tinst && tinst->needsCodegen()) + { + minst = tinst->minst; // cache result + assert(minst); + assert(minst->isRoot() || minst->rootImports()); + return true; + } + if (tnext && tnext->needsCodegen()) { minst = tnext->minst; // cache result - assert(!minst->isRoot()); - return false; + assert(minst); + assert(minst->isRoot() || minst->rootImports()); + return true; } + + // Bugzilla 2500 case + if (minst->rootImports()) + return true; + + // Elide codegen because this is not included in root instances. + return false; } else { + // Prefer instantiations from non-root module, to minimize object code size. + /* 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 minst imports any root modules, we still need to generate the code. + * But, if the non-root 'minst' imports any root modules, it might still need codegen. * * 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 * or the compilation of B do the actual instantiation? * - * See bugzilla 2500. + * See Bugzilla 2500. */ + if (!minst->isRoot() && !minst->rootImports()) + return false; + + TemplateInstance *tnext = this->tnext; + this->tnext = NULL; - if (!minst->rootImports()) + if (tnext && !tnext->needsCodegen() && tnext->minst) { - //printf("instantiated by %s %s\n", minst->toChars(), toChars()); + minst = tnext->minst; // cache result + assert(!minst->isRoot()); return false; } + + // Do codegen because this is not included in non-root instances. + return true; } - return true; } /* ======================== TemplateMixin ================================ */ diff --git a/test/runnable/extra-files/linkdebug.d b/test/runnable/extra-files/linkdebug.d new file mode 100644 index 000000000000..67da9dc2a230 --- /dev/null +++ b/test/runnable/extra-files/linkdebug.d @@ -0,0 +1,16 @@ +module linkdebug; + +void main() +{ + import linkdebug_uni; + import linkdebug_range; + + // OK + //SortedRangeX!(uint[], "a <= b") SR; + + CodepointSet set; + set.addInterval(1, 2); + + // NG, order dependent. + SortedRange!(uint[], "a <= b") SR; +} diff --git a/test/runnable/extra-files/linkdebug_primitives.d b/test/runnable/extra-files/linkdebug_primitives.d new file mode 100644 index 000000000000..81026d6345f4 --- /dev/null +++ b/test/runnable/extra-files/linkdebug_primitives.d @@ -0,0 +1,13 @@ +module linkdebug_primitives; + +size_t popBackN(R)(ref R r, size_t n) +{ + n = cast(size_t) (n < r.length ? n : r.length); + r = r[0 .. $ - n]; + return n; +} + +auto moveAt(R, I)(R r, I i) +{ + return r[i]; +} diff --git a/test/runnable/extra-files/linkdebug_range.d b/test/runnable/extra-files/linkdebug_range.d new file mode 100644 index 000000000000..ddf91bfdf92c --- /dev/null +++ b/test/runnable/extra-files/linkdebug_range.d @@ -0,0 +1,48 @@ +module linkdebug_range; + +import linkdebug_primitives : popBackN, moveAt; + +auto stride(R)(R r) +{ + static struct Result + { + R source; + + void popBack() + { + popBackN(source, 0); + } + + uint moveAt(size_t n) + { + return .moveAt(source, n); + } + } + return Result(r); +} + +struct SortedRange(Range, alias pred = "a < b") +{ + this(Range input) + out + { + dbgVerifySorted(); + } + body + { + } + + void dbgVerifySorted() + { + debug + { + uint[] _input; + auto st = stride(_input); + } + } +} + +auto assumeSorted(alias pred = "a < b", R)(R r) +{ + return SortedRange!(R, pred)(r); +} diff --git a/test/runnable/extra-files/linkdebug_uni.d b/test/runnable/extra-files/linkdebug_uni.d new file mode 100644 index 000000000000..248fae14217e --- /dev/null +++ b/test/runnable/extra-files/linkdebug_uni.d @@ -0,0 +1,19 @@ +module linkdebug_uni; + +import linkdebug_range; + +struct GcPolicy {} + +alias CodepointSet = InversionList!(); + +struct InversionList(SP = GcPolicy) +{ +@trusted: + size_t addInterval(int a, int b, size_t hint = 0) + { + auto data = new uint[](0); // affects to the number of missimg symbol + auto range = assumeSorted(data[]); // NG + //SortedRange!(uint[], "a < b") SR; // OK + return 1; + } +} diff --git a/test/runnable/linkdebug.sh b/test/runnable/linkdebug.sh new file mode 100755 index 000000000000..5b9488457a95 --- /dev/null +++ b/test/runnable/linkdebug.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +src=runnable${SEP}extra-files +dir=${RESULTS_DIR}${SEP}runnable +output_file=${dir}/linkdebug.sh.out + +if [ $OS == "win32" -o $OS == "win64" ]; then + LIBEXT=.lib +else + LIBEXT=.a +fi +libname=${dir}${SEP}libX${LIBEXT} + +$DMD -m${MODEL} -I${src} -of${libname} -lib ${src}${SEP}linkdebug_uni.d ${src}${SEP}linkdebug_range.d ${src}${SEP}linkdebug_primitives.d || exit 1 + +$DMD -m${MODEL} -I${src} -of${dir}${SEP}linkdebug${EXE} -g -debug ${src}${SEP}linkdebug.d ${libname} || exit 1 + +rm ${libname} +rm ${dir}/{linkdebug${OBJ},linkdebug${EXE}} + +echo Success > ${output_file} From cf56e5ad84164276f31f1d567ded97738a42191d Mon Sep 17 00:00:00 2001 From: k-hara Date: Thu, 27 Aug 2015 15:26:34 +0900 Subject: [PATCH 5/8] Elide codegen if TypeInfo is speculative Even if a struct is defined as non-root symbol, some built-in operations request its TypeInfo. For those, today TypeInfo_Struct is generated in COMDAT. On the other hand, TypeInfoDeclaration is always generated when the TypeInfo is requested, even if the code exists in speculative scope. By that, some unneeded TypeInfo objects had excessively generated. If a TypeInfo is not actually used, we should stop its generation. To satisfy two requirements, I added "speculative TypeInfo" concept associated with template instances. By this change, when a TypeInfo is *requested*, the codegen pass still visit it always. But if the corresponding type is instantiated struct or its derived, and is definitely speculative, the TypeInfo code also won't be emit into object file. --- src/aggregate.h | 5 ++ src/expression.c | 5 +- src/magicport.json | 1 + src/struct.c | 16 +++++- src/toobj.c | 6 ++ src/typinf.c | 134 ++++++++++++++++++++++++++++++++++++++------- 6 files changed, 143 insertions(+), 24 deletions(-) diff --git a/src/aggregate.h b/src/aggregate.h index d9271d30c0b1..299b59ad9882 100644 --- a/src/aggregate.h +++ b/src/aggregate.h @@ -170,6 +170,11 @@ class StructDeclaration : public AggregateDeclaration Type *arg1type; Type *arg2type; + // Even if struct is defined as non-root symbol, some built-in operations + // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo. + // For those, today TypeInfo_Struct is generated in COMDAT. + bool requestTypeInfo; + StructDeclaration(Loc loc, Identifier *id); Dsymbol *syntaxCopy(Dsymbol *s); void semantic(Scope *sc); diff --git a/src/expression.c b/src/expression.c index b64e5f150668..a8f2472b7d63 100644 --- a/src/expression.c +++ b/src/expression.c @@ -4161,7 +4161,7 @@ Expression *ArrayLiteralExp::semantic(Scope *sc) return new ErrorExp(); } - semanticTypeInfo(sc, t0); + semanticTypeInfo(sc, type); return this; } @@ -6114,6 +6114,9 @@ Expression *TypeidExp::semantic(Scope *sc) // Handle this in the glue layer e = new TypeidExp(loc, ta); e->type = getTypeInfoType(ta, sc); + + semanticTypeInfo(sc, ta); + if (ea) { e = new CommaExp(loc, ea, e); // execute ea diff --git a/src/magicport.json b/src/magicport.json index aadb3ab57b82..05ccb2903b60 100644 --- a/src/magicport.json +++ b/src/magicport.json @@ -248,6 +248,7 @@ "aggregate", "argtypes", "arraytypes", + "backend", "clone", "declaration", "dmodule", diff --git a/src/struct.c b/src/struct.c index 3e8cbe8fd882..36d25ef2fbe7 100644 --- a/src/struct.c +++ b/src/struct.c @@ -24,6 +24,7 @@ #include "template.h" #include "tokens.h" +Type *getTypeInfoType(Type *t, Scope *sc); TypeTuple *toArgTypes(Type *t); FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals @@ -105,11 +106,19 @@ void semanticTypeInfo(Scope *sc, Type *t) if (ti->minst && !ti->minst->isRoot()) Module::addDeferredSemantic3(sd); } - else if (sd->inNonRoot()) + else { - //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot()); - Module::addDeferredSemantic3(sd); + if (sd->inNonRoot()) + { + //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot()); + Module::addDeferredSemantic3(sd); + } } + + getTypeInfoType(t, sc); + + if (sc->minst) + sd->requestTypeInfo = true; } void visit(TypeClass *t) { } void visit(TypeTuple *t) @@ -667,6 +676,7 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id) ispod = ISPODfwd; arg1type = NULL; arg2type = NULL; + requestTypeInfo = false; // For forward references type = new TypeStruct(this); diff --git a/src/toobj.c b/src/toobj.c index f294ca0f745f..2209e3e1b77a 100644 --- a/src/toobj.c +++ b/src/toobj.c @@ -58,6 +58,7 @@ Symbol *toVtblSymbol(ClassDeclaration *cd); Symbol *toInitializer(AggregateDeclaration *ad); Symbol *toInitializer(EnumDeclaration *ed); void genTypeInfo(Type *t, Scope *sc); +bool isSpeculativeType(Type *t); void toDebug(EnumDeclaration *ed); void toDebug(StructDeclaration *sd); @@ -1010,6 +1011,11 @@ void toObjFile(Dsymbol *ds, bool multiobj) void visit(TypeInfoDeclaration *tid) { + if (isSpeculativeType(tid->tinfo)) + { + //printf("-speculative '%s'\n", tid->toPrettyChars()); + return; + } //printf("TypeInfoDeclaration::toObjFile(%p '%s') protection %d\n", tid, tid->toChars(), tid->protection); if (multiobj) diff --git a/src/typinf.c b/src/typinf.c index 6cdf597e6f0a..dc545f6161bd 100644 --- a/src/typinf.c +++ b/src/typinf.c @@ -78,19 +78,9 @@ void genTypeInfo(Type *torig, Scope *sc) // Generate COMDAT if (sc) // if in semantic() pass { - if (sc->func && sc->func->inNonRoot()) - { - // Bugzilla 13043: Avoid linking TypeInfo if it's not - // necessary for root module compilation - } - else - { - // Find module that will go all the way to an object file - Module *m = sc->module->importedFrom; - m->members->push(t->vtinfo); - - semanticTypeInfo(sc, t); - } + // Find module that will go all the way to an object file + Module *m = sc->module->importedFrom; + m->members->push(t->vtinfo); } else // if in obj generation pass { @@ -135,6 +125,96 @@ TypeInfoDeclaration *getTypeInfoDeclaration(Type *t) } } +bool isSpeculativeType(Type *t) +{ + class SpeculativeTypeVisitor : public Visitor + { + public: + StructDeclaration *result; + + SpeculativeTypeVisitor() : result(NULL) {} + + void visit(Type *t) + { + Type *tb = t->toBasetype(); + if (tb != t) + tb->accept(this); + } + void visit(TypeNext *t) + { + if (t->next) + t->next->accept(this); + } + void visit(TypeBasic *t) { } + void visit(TypeVector *t) + { + t->basetype->accept(this); + } + void visit(TypeAArray *t) + { + t->index->accept(this); + visit((TypeNext *)t); + } + void visit(TypeFunction *t) + { + visit((TypeNext *)t); + // Currently TypeInfo_Function doesn't store parameter types. + } + void visit(TypeStruct *t) + { + result = t->sym; + } + void visit(TypeClass *t) { } + void visit(TypeTuple *t) + { + if (t->arguments) + { + for (size_t i = 0; i < t->arguments->dim; i++) + { + Type *tprm = (*t->arguments)[i]->type; + if (tprm) + tprm->accept(this); + } + } + } + }; + SpeculativeTypeVisitor v; + t->accept(&v); + + StructDeclaration *sd = v.result; + if (!sd) + return false; + + if (TemplateInstance *ti = sd->isInstantiated()) + { + if (!ti->needsCodegen()) + { + /* Bugzilla 14425: TypeInfo_Struct would refer the members of + * struct (e.g. opEquals via xopEquals field), so if it's instantiated + * in speculative context, TypeInfo creation should also be + * stopped to avoid 'unresolved symbol' linker errors. + */ + if (!ti->minst) + return true; + + /* When -debug/-unittest is specified, all of non-root instances are + * automatically changed to speculative, and here is always reached + * from those instantiated non-root structs. + * Therefore, if the TypeInfo is not auctually requested, + * we have to elide its codegen. + */ + if (!sd->requestTypeInfo) + return true; + } + } + else + { + //assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid? + } + + return false; +} + /**************************************************** */ @@ -422,13 +502,27 @@ class TypeInfoDtVisitor : public Visitor if (TemplateInstance *ti = sd->isInstantiated()) { - /* Bugzilla 14425: TypeInfo_Struct would refer the members of - * struct (e.g. opEquals via xopEquals field), so if it's instantiated - * in speculative context, TypeInfo creation should also be - * stopped to avoid 'unresolved symbol' linker errors. - */ - if (!ti->needsCodegen() && !ti->minst) - return; + if (!ti->needsCodegen()) + { + assert(ti->minst); + assert(sd->requestTypeInfo); + + /* ti->toObjFile() won't get called. So, store these + * member functions into object file in here. + */ + if (sd->xeq && sd->xeq != StructDeclaration::xerreq) + toObjFile(sd->xeq, global.params.multiobj); + if (sd->xcmp && sd->xcmp != StructDeclaration::xerrcmp) + toObjFile(sd->xcmp, global.params.multiobj); + if (FuncDeclaration *ftostr = search_toString(sd)) + toObjFile(ftostr, global.params.multiobj); + if (sd->xhash) + toObjFile(sd->xhash, global.params.multiobj); + if (sd->postblit) + toObjFile(sd->postblit, global.params.multiobj); + if (sd->dtor) + toObjFile(sd->dtor, global.params.multiobj); + } } /* Put out: From 0b45d187c8ddb7442e4f7a9fc8780bb1ac58dbbc Mon Sep 17 00:00:00 2001 From: k-hara Date: Thu, 27 Aug 2015 15:26:39 +0900 Subject: [PATCH 6/8] More TypeInfo objects would be requested from inlined functions 1. When a non-root function is succeeded to expand for inlining, the additional TypeInfo requests should be handled properly. 2. When a function's semantic3 pass will get called from `canInline`, it would generate more speculative instances when -debug/-unittest is specified. If the additionally instantiated function succeeds to expand, it should be changed to non-speculative, because its code is reachable from the final executable. --- src/inline.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/struct.c | 11 +++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/inline.c b/src/inline.c index 5422521b962c..c2ad43a7db0c 100644 --- a/src/inline.c +++ b/src/inline.c @@ -919,6 +919,26 @@ Expression *doInline(Expression *e, InlineDoState *ids) ne->newargs = arrayExpressiondoInline(e->newargs); ne->arguments = arrayExpressiondoInline(e->arguments); result = ne; + + semanticTypeInfo(NULL, e->type); + } + + void visit(DeleteExp *e) + { + visit((UnaExp *)e); + + Type *tb = e->e1->type->toBasetype(); + if (tb->ty == Tarray) + { + Type *tv = tb->nextOf()->baseElemOf(); + if (tv->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->dtor) + semanticTypeInfo(NULL, ts); + } + } } void visit(UnaExp *e) @@ -957,6 +977,37 @@ Expression *doInline(Expression *e, InlineDoState *ids) result = ce; } + void visit(AssignExp *e) + { + visit((BinExp *)e); + + if (e->e1->op == TOKarraylength) + { + ArrayLengthExp *ale = (ArrayLengthExp *)e->e1; + Type *tn = ale->e1->type->toBasetype()->nextOf(); + semanticTypeInfo(NULL, tn); + } + } + + void visit(EqualExp *e) + { + visit((BinExp *)e); + + Type *t1 = e->e1->type->toBasetype(); + if (t1->ty == Tarray || t1->ty == Tsarray) + { + Type *t = t1->nextOf()->toBasetype(); + while (t->toBasetype()->nextOf()) + t = t->nextOf()->toBasetype(); + if (t->ty == Tstruct) + semanticTypeInfo(NULL, t); + } + else if (t1->ty == Taarray) + { + semanticTypeInfo(NULL, t1); + } + } + void visit(IndexExp *e) { IndexExp *are = (IndexExp *)e->copy(); @@ -1042,6 +1093,8 @@ Expression *doInline(Expression *e, InlineDoState *ids) ce->elements = arrayExpressiondoInline(e->elements); result = ce; + + semanticTypeInfo(NULL, e->type); } void visit(AssocArrayLiteralExp *e) @@ -1051,6 +1104,8 @@ Expression *doInline(Expression *e, InlineDoState *ids) ce->keys = arrayExpressiondoInline(e->keys); ce->values = arrayExpressiondoInline(e->values); result = ce; + + semanticTypeInfo(NULL, e->type); } void visit(StructLiteralExp *e) @@ -1838,6 +1893,14 @@ static Expression *expandInline(FuncDeclaration *fd, FuncDeclaration *parent, ids.parent = parent; ids.fd = fd; + // When the function is actually expanded + if (TemplateInstance *ti = fd->isInstantiated()) + { + // change ti to non-speculative instance + if (!ti->minst) + ti->minst = ti->tempdecl->getModule(); + } + if (ps) as = new Statements(); diff --git a/src/struct.c b/src/struct.c index 36d25ef2fbe7..6fcb20481cd3 100644 --- a/src/struct.c +++ b/src/struct.c @@ -115,9 +115,16 @@ void semanticTypeInfo(Scope *sc, Type *t) } } - getTypeInfoType(t, sc); + if (!sc) // inline may request TypeInfo. + { + Scope scx; + scx.module = sd->getModule(); + getTypeInfoType(t, &scx); + } + else + getTypeInfoType(t, sc); - if (sc->minst) + if (!sc || sc->minst) sd->requestTypeInfo = true; } void visit(TypeClass *t) { } From cd29d5f34cbacebd83ecdb9727ab1b93f7fa6af1 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sun, 19 Jul 2015 10:52:00 +0200 Subject: [PATCH 7/8] Merge pull request #4814 from 9rnsr/fix14541 Issue 14541 - "duplicate COMDAT" linker error with the template forward reference in Tuple.opAssign --- src/template.c | 23 +++++++++++ test/runnable/imports/link14541traits.d | 54 +++++++++++++++++++++++++ test/runnable/link14541.d | 42 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 test/runnable/imports/link14541traits.d create mode 100644 test/runnable/link14541.d diff --git a/src/template.c b/src/template.c index fcc128ca2179..89750556d45c 100644 --- a/src/template.c +++ b/src/template.c @@ -5911,6 +5911,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) * implements the typeargs. If so, just refer to that one instead. */ inst = tempdecl->findExistingInstance(this, fargs); + TemplateInstance *errinst = NULL; if (!inst) { // So, we need to implement 'this' instance. @@ -5919,6 +5920,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) { // If the first instantiation had failed, re-run semantic, // so that error messages are shown. + errinst = inst; } else { @@ -6287,6 +6289,27 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) symtab = NULL; } } + else if (errinst) + { + /* Bugzilla 14541: If the previous gagged instance had failed by + * circular references, currrent "error reproduction instantiation" + * might succeed, because of the difference of instantiated context. + * On such case, the cached error instance needs to be overridden by the + * succeeded instance. + */ + size_t bi = hash % tempdecl->buckets.dim; + TemplateInstances *instances = tempdecl->buckets[bi]; + assert(instances); + for (size_t i = 0; i < instances->dim; i++) + { + TemplateInstance *ti = (*instances)[i]; + if (ti == errinst) + { + (*instances)[i] = this; // override + break; + } + } + } #if LOG printf("-TemplateInstance::semantic('%s', this=%p)\n", toChars(), this); diff --git a/test/runnable/imports/link14541traits.d b/test/runnable/imports/link14541traits.d new file mode 100644 index 000000000000..de248494614c --- /dev/null +++ b/test/runnable/imports/link14541traits.d @@ -0,0 +1,54 @@ +module imports.link14541traits; + +template hasElaborateAssign(S) +{ + static if (is(S == struct)) + { + extern __gshared S lvalue; + + enum hasElaborateAssign = is(typeof(S.init.opAssign(S.init))) || + is(typeof(S.init.opAssign(lvalue))); + } + else + { + enum bool hasElaborateAssign = false; + } +} + +void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc +{ + static if (hasElaborateAssign!T) + { + } + else + { + } +} + +template Tuple(Types...) +{ + struct Tuple + { + Types field; + alias field this; + + this(Types values) + { + field[] = values[]; + } + + void opAssign(R)(auto ref R rhs) + { + static if (is(R : Tuple!Types) && !__traits(isRef, rhs)) + { + // Use swap-and-destroy to optimize rvalue assignment + swap!(Tuple!Types)(this, rhs); + } + else + { + // Do not swap; opAssign should be called on the fields. + field[] = rhs.field[]; + } + } + } +} diff --git a/test/runnable/link14541.d b/test/runnable/link14541.d new file mode 100644 index 000000000000..433b9d739507 --- /dev/null +++ b/test/runnable/link14541.d @@ -0,0 +1,42 @@ +import imports.link14541traits; + +void main() +{ + Tuple!(int, int) result; + + alias T = typeof(result); + static assert(hasElaborateAssign!T); + // hasElaborateAssign!(Tuple(int, int)): + // 1. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue] + // 2. instantiates swap!(Tuple!(int, int)) + // 3. instantiates hasElaborateAssign!(Tuple!(int, int)) + // --> forward reference error + // --> swap!(Tuple!(int, int)) fails to instantiate + // --> Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = rvalue] fails to instantiate + // 4. instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Lvalue] + // --> succeeds + // hasElaborateAssign!(Tuple(int, int)) succeeds to instantiate (result is 'true') + + // Instantiates Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue], but + // it's already done in gagged context, so this is made an error reproduction instantiation. + // But, the forward reference of hasElaborateAssign!(Tuple(int, int)) is already resolved, so + // the instantiation will succeeds. + result = Tuple!(int, int)(0, 0); // --> 1st error reproduction instantiation + result = Tuple!(int, int)(0, 0); // --> 2nd error reproduction instantiation + + // The two error reproduction instantiations generate the function: + // Tuple!(int, int).opAssign!(Tuple!(int, int)) [auto ref = Rvalue] + // twice, then it will cause duplicate COMDAT error in Win64 platform. +} + +/+ +The point is, if instantiated contexts are different, two instantiations may cause different result. + +- The 1st Tuple.opAssign instantiation is invoked from hasElaborateAssign template with gagging. + So it has failed, because of the circular reference of hasElaborateAssign template.. + +- The 2nd Tuple.opAssign instantiation is invoked from main() without gagging. + It does not have circular reference, so the instantiation should succeed. + +Therefore, the gagged failure should be overridden by the ungagged success. ++/ From 083ea4a15049a25c4837d4720173d7b609d27916 Mon Sep 17 00:00:00 2001 From: k-hara Date: Sun, 30 Aug 2015 21:34:21 +0900 Subject: [PATCH 8/8] Fix isSpeculativeType logic --- src/typinf.c | 74 +++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/typinf.c b/src/typinf.c index dc545f6161bd..089dcbf98491 100644 --- a/src/typinf.c +++ b/src/typinf.c @@ -130,9 +130,9 @@ bool isSpeculativeType(Type *t) class SpeculativeTypeVisitor : public Visitor { public: - StructDeclaration *result; + bool result; - SpeculativeTypeVisitor() : result(NULL) {} + SpeculativeTypeVisitor() : result(false) {} void visit(Type *t) { @@ -162,7 +162,39 @@ bool isSpeculativeType(Type *t) } void visit(TypeStruct *t) { - result = t->sym; + StructDeclaration *sd = t->sym; + if (TemplateInstance *ti = sd->isInstantiated()) + { + if (!ti->needsCodegen()) + { + /* Bugzilla 14425: TypeInfo_Struct would refer the members of + * struct (e.g. opEquals via xopEquals field), so if it's instantiated + * in speculative context, TypeInfo creation should also be + * stopped to avoid 'unresolved symbol' linker errors. + */ + if (!ti->minst) + { + result |= true; + return; + } + + /* When -debug/-unittest is specified, all of non-root instances are + * automatically changed to speculative, and here is always reached + * from those instantiated non-root structs. + * Therefore, if the TypeInfo is not auctually requested, + * we have to elide its codegen. + */ + if (!sd->requestTypeInfo) + { + result |= true; + return; + } + } + } + else + { + //assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid? + } } void visit(TypeClass *t) { } void visit(TypeTuple *t) @@ -174,45 +206,15 @@ bool isSpeculativeType(Type *t) Type *tprm = (*t->arguments)[i]->type; if (tprm) tprm->accept(this); + if (result) + return; } } } }; SpeculativeTypeVisitor v; t->accept(&v); - - StructDeclaration *sd = v.result; - if (!sd) - return false; - - if (TemplateInstance *ti = sd->isInstantiated()) - { - if (!ti->needsCodegen()) - { - /* Bugzilla 14425: TypeInfo_Struct would refer the members of - * struct (e.g. opEquals via xopEquals field), so if it's instantiated - * in speculative context, TypeInfo creation should also be - * stopped to avoid 'unresolved symbol' linker errors. - */ - if (!ti->minst) - return true; - - /* When -debug/-unittest is specified, all of non-root instances are - * automatically changed to speculative, and here is always reached - * from those instantiated non-root structs. - * Therefore, if the TypeInfo is not auctually requested, - * we have to elide its codegen. - */ - if (!sd->requestTypeInfo) - return true; - } - } - else - { - //assert(!sd->inNonRoot() || sd->requestTypeInfo); // valid? - } - - return false; + return v.result; } /****************************************************