From: Iain Buclaw Date: Fri, 23 Oct 2020 07:41:11 +0000 (+0200) Subject: d: Fix undefined template references with circular module imports X-Git-Tag: releases/gcc-10.3.0~624 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=96c4451b3284704567d9704bc5e91e7a1f307818;p=thirdparty%2Fgcc.git d: Fix undefined template references with circular module imports In `TemplateInstance::semantic`, there exists special handling of matching template instances for the same template declaration to ensure that only at most one instance gets codegen'd. If the primary instance `inst` originated from a non-root module, the `minst` field will be updated so it is now coming from a root module, however all Dsymbol `inst->members` of the instance still have their `_scope->minst` pointing at the original non-root module. We must now propagate `minst` to all members so that forward referenced dependencies that get instantiated will also be appended to the root module, otherwise there will be undefined references at link-time. This doesn't affect compilations where all modules are compiled together, as every module is a root module in that situation. What this primarily affects are cases where there is a mix of root and non-root modules, and a template was first instantiated in a non-root context, then later instantiated again in a root context. gcc/d/ChangeLog: * dmd/dtemplate.c (TemplateInstance::semantic): Propagate the root module where the instantiated template should belong from the instance to all member scopes. gcc/testsuite/ChangeLog: * gdc.test/compilable/imports/test21299/func.d: New test. * gdc.test/compilable/imports/test21299/mtype.d: New test. * gdc.test/compilable/imports/test21299/rootstringtable.d: New test. * gdc.test/compilable/test21299a.d: New test. * gdc.test/compilable/test21299b.d: New test. * gdc.test/compilable/test21299c.d: New test. * gdc.test/compilable/test21299d.d: New test. (cherry picked from commit e419ede8915eeb879de3d9c026cd4213aaceb86a) --- diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c index 9d211eb0340d..cba2beea0dd3 100644 --- a/gcc/d/dmd/dtemplate.c +++ b/gcc/d/dmd/dtemplate.c @@ -33,6 +33,7 @@ #include "hdrgen.h" #include "id.h" #include "attrib.h" +#include "cond.h" #include "tokens.h" #define IDX_NOTFOUND (0x12345678) // index is not found @@ -6088,17 +6089,18 @@ Lerror: if (minst && minst->isRoot() && !(inst->minst && inst->minst->isRoot())) { /* Swap the position of 'inst' and 'this' in the instantiation graph. - * Then, the primary instance `inst` will be changed to a root instance. + * Then, the primary instance `inst` will be changed to a root instance, + * along with all members of `inst` having their scopes updated. * * Before: - * non-root -> A!() -> B!()[inst] -> C!() + * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] } * | * root -> D!() -> B!()[this] * * After: * non-root -> A!() -> B!()[this] * | - * root -> D!() -> B!()[inst] -> C!() + * root -> D!() -> B!()[inst] -> C!() { members[root] } */ Module *mi = minst; TemplateInstance *ti = tinst; @@ -6107,6 +6109,64 @@ Lerror: inst->minst = mi; inst->tinst = ti; + /* https://issues.dlang.org/show_bug.cgi?id=21299 + `minst` has been updated on the primary instance `inst` so it is + now coming from a root module, however all Dsymbol `inst.members` + of the instance still have their `_scope.minst` pointing at the + original non-root module. We must now propagate `minst` to all + members so that forward referenced dependencies that get + instantiated will also be appended to the root module, otherwise + there will be undefined references at link-time. */ + class InstMemberWalker : public Visitor + { + public: + TemplateInstance *inst; + + InstMemberWalker(TemplateInstance *inst) + : inst(inst) { } + + void visit(Dsymbol *d) + { + if (d->_scope) + d->_scope->minst = inst->minst; + } + + void visit(ScopeDsymbol *sds) + { + if (!sds->members) + return; + for (size_t i = 0; i < sds->members->dim; i++) + { + Dsymbol *s = (*sds->members)[i]; + s->accept(this); + } + visit((Dsymbol *)sds); + } + + void visit(AttribDeclaration *ad) + { + Dsymbols *d = ad->include(NULL, NULL); + if (!d) + return; + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + s->accept(this); + } + visit((Dsymbol *)ad); + } + + void visit(ConditionalDeclaration *cd) + { + if (cd->condition->inc) + visit((AttribDeclaration *)cd); + else + visit((Dsymbol *)cd); + } + }; + InstMemberWalker v(inst); + inst->accept(&v); + if (minst) // if inst was not speculative { /* Add 'inst' once again to the root module members[], then the diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21299/func.d b/gcc/testsuite/gdc.test/compilable/imports/test21299/func.d new file mode 100644 index 000000000000..fe3321faff77 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/test21299/func.d @@ -0,0 +1,8 @@ +module imports.test21299.func; +import imports.test21299.mtype; +import imports.test21299.rootstringtable; +class FuncDeclaration { + StringTable!Type stringtable; + StringTable2!Type stringtable2; + StringTable3!Type stringtable3; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21299/mtype.d b/gcc/testsuite/gdc.test/compilable/imports/test21299/mtype.d new file mode 100644 index 000000000000..01bac82c2e4f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/test21299/mtype.d @@ -0,0 +1,8 @@ +module imports.test21299.mtype; +import imports.test21299.func; +import imports.test21299.rootstringtable; +class Type { + StringTable!Type stringtable; + StringTable2!Type stringtable2; + StringTable3!Type stringtable3; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21299/rootstringtable.d b/gcc/testsuite/gdc.test/compilable/imports/test21299/rootstringtable.d new file mode 100644 index 000000000000..12a2d92899b4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/test21299/rootstringtable.d @@ -0,0 +1,96 @@ +module imports.test21299.rootstringtable; +struct StringValue(T) +{ + char* lstring() + { + return cast(char*)&this; + } +} + +struct StringTable(T) +{ + StringValue!T* insert() + { + allocValue; + return getValue; + } + + uint allocValue() + { + StringValue!(T) sv; + sv.lstring[0] = 0; + return 0; + } + + StringValue!T* getValue() + { + return cast(StringValue!T*)&this; + } +} + +// Other tests are the same as the original issue, but use other kinds of +// nesting Dsymbols that need to be handled by templateInstanceSemantic(). +struct StringValue2(T) +{ + char* lstring() + { + return cast(char*)&this; + } +} + +struct StringTable2(T) +{ + @nogc // AttribDeclaration (also covers pragma, extern(), static foreach, ...) + { + StringValue2!T* insert() + { + allocValue; + return getValue; + } + + uint allocValue() + { + StringValue2!(T) sv; + sv.lstring[0] = 0; + return 0; + } + + StringValue2!T* getValue() + { + return cast(StringValue2!T*)&this; + } + } +} + +// +struct StringValue3(T) +{ + char* lstring() + { + return cast(char*)&this; + } +} + +struct StringTable3(T) +{ + static if (true) // ConditionalDeclaration (static if) + { + StringValue3!T* insert() + { + allocValue; + return getValue; + } + + uint allocValue() + { + StringValue3!(T) sv; + sv.lstring[0] = 0; + return 0; + } + + StringValue3!T* getValue() + { + return cast(StringValue3!T*)&this; + } + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test21299a.d b/gcc/testsuite/gdc.test/compilable/test21299a.d new file mode 100644 index 000000000000..049ee6ae35be --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21299a.d @@ -0,0 +1,4 @@ +// EXTRA_SOURCES: imports/test21299/mtype.d imports/test21299/rootstringtable.d +// REQUIRED_ARGS: -main +// LINK +module test21299a; diff --git a/gcc/testsuite/gdc.test/compilable/test21299b.d b/gcc/testsuite/gdc.test/compilable/test21299b.d new file mode 100644 index 000000000000..b9d992acfb95 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21299b.d @@ -0,0 +1,4 @@ +// EXTRA_SOURCES: imports/test21299/func.d imports/test21299/rootstringtable.d +// REQUIRED_ARGS: -main +// LINK: +module test21299b; diff --git a/gcc/testsuite/gdc.test/compilable/test21299c.d b/gcc/testsuite/gdc.test/compilable/test21299c.d new file mode 100644 index 000000000000..88ed21f3ea6c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21299c.d @@ -0,0 +1,5 @@ +// EXTRA_SOURCES: imports/test21299/mtype.d imports/test21299/func.d imports/test21299/rootstringtable.d +// COMPILE_SEPARATELY: +// LINK: +module test21299c; +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/test21299d.d b/gcc/testsuite/gdc.test/compilable/test21299d.d new file mode 100644 index 000000000000..67ec60a582b7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21299d.d @@ -0,0 +1,27 @@ +// REQUIRED_ARGS: -main +// LINK: +module test21299d; + +struct DefaultPredicates +{ + struct IsEqual(T) + { + static opCall(in T, in T) + { + return 0; + } + } +} + +void moveToEnd(T, Pred = DefaultPredicates.IsEqual!T)(T[] array, T element, Pred pred = Pred.init) +{ + pred(array[0], element); +} + +class Task +{ + void removeTerminationHook(void delegate() hook) + { + moveToEnd([], hook); + } +}