From e419ede8915eeb879de3d9c026cd4213aaceb86a Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Fri, 23 Oct 2020 09:41:11 +0200 Subject: [PATCH] d: Merge upstream dmd 0fcdaab32 Fixes a bug where there was undefined template references when compiling upstream dmd mainline. 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. Reviewed-on: https://github.com/dlang/dmd/pull/11867 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 0fcdaab32 --- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/dtemplate.c | 66 ++++++++++++- .../compilable/imports/test21299/func.d | 8 ++ .../compilable/imports/test21299/mtype.d | 8 ++ .../imports/test21299/rootstringtable.d | 96 +++++++++++++++++++ .../gdc.test/compilable/test21299a.d | 4 + .../gdc.test/compilable/test21299b.d | 4 + .../gdc.test/compilable/test21299c.d | 5 + .../gdc.test/compilable/test21299d.d | 27 ++++++ 9 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gdc.test/compilable/imports/test21299/func.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/test21299/mtype.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/test21299/rootstringtable.d create mode 100644 gcc/testsuite/gdc.test/compilable/test21299a.d create mode 100644 gcc/testsuite/gdc.test/compilable/test21299b.d create mode 100644 gcc/testsuite/gdc.test/compilable/test21299c.d create mode 100644 gcc/testsuite/gdc.test/compilable/test21299d.d diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 5f6193f76b7..7b561e4044e 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -70aabfb511d55f2bfbdccbac7868519d9d4b63da +0fcdaab32c7645820820f6e1474343ccfb7560e5 The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c index a86daeee633..caa8a5ba9f4 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->length; i++) + { + Dsymbol *s = (*sds->members)[i]; + s->accept(this); + } + visit((Dsymbol *)sds); + } + + void visit(AttribDeclaration *ad) + { + Dsymbols *d = ad->include(NULL); + if (!d) + return; + for (size_t i = 0; i < d->length; 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 00000000000..fe3321faff7 --- /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 00000000000..01bac82c2e4 --- /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 00000000000..12a2d92899b --- /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 00000000000..049ee6ae35b --- /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 00000000000..b9d992acfb9 --- /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 00000000000..88ed21f3ea6 --- /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 00000000000..67ec60a582b --- /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); + } +} -- 2.30.2