[gcc r10-9040] d: Fix undefined template references with circular module imports
Iain Buclaw
ibuclaw@gcc.gnu.org
Wed Nov 18 10:32:32 GMT 2020
https://gcc.gnu.org/g:96c4451b3284704567d9704bc5e91e7a1f307818
commit r10-9040-g96c4451b3284704567d9704bc5e91e7a1f307818
Author: Iain Buclaw <ibuclaw@gdcproject.org>
Date: Fri Oct 23 09:41:11 2020 +0200
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:
---
gcc/d/dmd/dtemplate.c | 66 ++++++++++++++-
.../gdc.test/compilable/imports/test21299/func.d | 8 ++
.../gdc.test/compilable/imports/test21299/mtype.d | 8 ++
.../compilable/imports/test21299/rootstringtable.d | 96 ++++++++++++++++++++++
gcc/testsuite/gdc.test/compilable/test21299a.d | 4 +
gcc/testsuite/gdc.test/compilable/test21299b.d | 4 +
gcc/testsuite/gdc.test/compilable/test21299c.d | 5 ++
gcc/testsuite/gdc.test/compilable/test21299d.d | 27 ++++++
8 files changed, 215 insertions(+), 3 deletions(-)
diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c
index 9d211eb0340..cba2beea0dd 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 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);
+ }
+}
More information about the Gcc-cvs
mailing list