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 6d4bfa5f7b46..33cf9e3d88f6 100644 --- a/src/expression.c +++ b/src/expression.c @@ -4156,7 +4156,7 @@ Expression *ArrayLiteralExp::semantic(Scope *sc) return new ErrorExp(); } - semanticTypeInfo(sc, t0); + semanticTypeInfo(sc, type); return this; } @@ -6112,6 +6112,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/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/magicport.json b/src/magicport.json index 4a8780cc18d3..7f01e311a57e 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 ec186e52ff17..6fcb20481cd3 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 @@ -100,12 +101,31 @@ 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()) { - //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot()); - Module::addDeferredSemantic3(sd); + 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); + } + } + + if (!sc) // inline may request TypeInfo. + { + Scope scx; + scx.module = sd->getModule(); + getTypeInfoType(t, &scx); + } + else + getTypeInfoType(t, sc); + + if (!sc || sc->minst) + sd->requestTypeInfo = true; } void visit(TypeClass *t) { } void visit(TypeTuple *t) @@ -663,6 +683,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/template.c b/src/template.c index 62535bd59b7a..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 { @@ -5942,22 +5944,46 @@ 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) + /* 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())) { - // 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 +6005,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); @@ -6319,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); @@ -7288,6 +7279,64 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) return nested != 0; } +/***************************************** + * Append 'this' to the specific module members[] + */ +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 + * 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 + { + /* 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; + 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 @@ -7837,15 +7886,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, @@ -7876,9 +7918,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. @@ -7893,51 +7938,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; - if (!minst->rootImports()) + TemplateInstance *tnext = this->tnext; + this->tnext = NULL; + + 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/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/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..089dcbf98491 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,98 @@ TypeInfoDeclaration *getTypeInfoDeclaration(Type *t) } } +bool isSpeculativeType(Type *t) +{ + class SpeculativeTypeVisitor : public Visitor + { + public: + bool result; + + SpeculativeTypeVisitor() : result(false) {} + + 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) + { + 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) + { + if (t->arguments) + { + for (size_t i = 0; i < t->arguments->dim; i++) + { + Type *tprm = (*t->arguments)[i]->type; + if (tprm) + tprm->accept(this); + if (result) + return; + } + } + } + }; + SpeculativeTypeVisitor v; + t->accept(&v); + return v.result; +} + /**************************************************** */ @@ -422,13 +504,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: 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/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/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/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/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}} 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. ++/ 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} 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); +}