aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Zeitlin <vz-swig@zeitlins.org>2018-11-21 00:07:14 +0100
committerVadim Zeitlin <vz-swig@zeitlins.org>2018-11-24 19:21:47 +0100
commit30bb977a6436fa7903aca041606c1110d37a2401 (patch)
tree14a0484de7d2824236e934e93e6ad5170c526a29
parent0f61c5a84743268d808bd8da372a27f8e2e88cbc (diff)
downloadswig-30bb977a6436fa7903aca041606c1110d37a2401.tar.gz
Fix handling of abstract base classes nested inside templates
Code handling %template in the parser created a totally new top-level module child of namespace type when handling templates inside a namespace and copied the nodes from the previously parsed C++ template declaration to it. However copies of this node kept their original values of "abstracts" attribute, which contained pointers to the classes in the original template declaration, i.e. outside of the subtree created for the instantiated template. This, in turn, meant that during the types resolution pass, the code in TypePass did not update the types used in the methods of the classes appearing in the "abstracts" List, even though it did update the types for the children of the instantiated template subtree. And this finally resulted in wrongly detecting overridden virtual methods as abstract in Allocate::is_abstract_inherit() during the next pass, as the signatures of the overridden method -- using resolved types -- and of the method from the class pointed to by "abstract" -- using the original types from C++ code -- didn't match. Resolve this simply by not copying "abstracts" attributes when creating the template subtree and doing another pass over this tree to recreate them using the new nodes, just as it's already done for "defaultargs" attribute, presumably for similar reasons. Note that doing another pass over the tree is not as efficient as doing everything in a single pass, but merging the new update_abstracts() with update_defaultargs() is not completely obvious, so for now keep it simple and optimize it later if necessary. Also, add a test checking for the situation described above. Closes #1353.
-rw-r--r--CHANGES.current8
-rw-r--r--Examples/test-suite/csharp/nested_in_template_runme.cs10
-rw-r--r--Examples/test-suite/nested_in_template.i34
-rw-r--r--Examples/test-suite/python/nested_in_template_runme.py5
-rw-r--r--Source/CParse/parser.y22
5 files changed, 79 insertions, 0 deletions
diff --git a/CHANGES.current b/CHANGES.current
index 2eb05fe25..f32583568 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,14 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.0.0 (in progress)
===========================
+2018-11-24: vadz
+ #1358 Fix handling of abstract base classes nested inside templates
+
+ Correct detecting of whether a derived class method overrides a pure virtual
+ base class method when both classes are nested inside a template class: this
+ notably didn't work correctly for methods taking parameters of the base class
+ type.
+
2018-11-22: rupertnash
[Python] #1282 Make generated module runnable via python -m (PEP 366 conforming)
diff --git a/Examples/test-suite/csharp/nested_in_template_runme.cs b/Examples/test-suite/csharp/nested_in_template_runme.cs
new file mode 100644
index 000000000..6d6f2e8a1
--- /dev/null
+++ b/Examples/test-suite/csharp/nested_in_template_runme.cs
@@ -0,0 +1,10 @@
+using System;
+using nested_in_templateNamespace;
+
+public class runme {
+ static void Main() {
+ var cd = new OuterTemplate1.ConcreteDerived(8.8);
+ if (cd.m_value != 8.8)
+ throw new Exception("ConcreteDerived not created correctly");
+ }
+}
diff --git a/Examples/test-suite/nested_in_template.i b/Examples/test-suite/nested_in_template.i
new file mode 100644
index 000000000..d878c62b0
--- /dev/null
+++ b/Examples/test-suite/nested_in_template.i
@@ -0,0 +1,34 @@
+%module nested_in_template
+
+#if !defined(SWIGCSHARP) && !defined(SWIGJAVA)
+%feature("flatnested");
+#endif
+
+%inline %{
+template <int>
+struct OuterTemplate;
+
+template <>
+struct OuterTemplate<1>
+{
+ struct AbstractBase
+ {
+ virtual bool IsSameAs(const AbstractBase& other) const = 0;
+ virtual ~AbstractBase() {}
+ };
+
+ struct ConcreteDerived : AbstractBase
+ {
+ ConcreteDerived() : m_value(0.) {}
+ explicit ConcreteDerived(double value) : m_value(value) {}
+
+ virtual bool IsSameAs(const AbstractBase& other) const {
+ return m_value == static_cast<const ConcreteDerived&>(other).m_value;
+ }
+
+ double m_value;
+ };
+};
+%}
+
+%template(OuterTemplate1) OuterTemplate<1>;
diff --git a/Examples/test-suite/python/nested_in_template_runme.py b/Examples/test-suite/python/nested_in_template_runme.py
new file mode 100644
index 000000000..b4aa25b9b
--- /dev/null
+++ b/Examples/test-suite/python/nested_in_template_runme.py
@@ -0,0 +1,5 @@
+from nested_in_template import *
+
+cd = ConcreteDerived(8.8)
+if cd.m_value != 8.8:
+ raise RuntimeError("ConcreteDerived not created correctly")
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index 91ab94c52..ee121abec 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -166,6 +166,11 @@ static Node *copy_node(Node *n) {
Setattr(nn, "needs_defaultargs", "1");
continue;
}
+ /* same for abstracts, which contains pointers to the source node children, and so will need to be patch too */
+ if (strcmp(ckey,"abstracts") == 0) {
+ SetFlag(nn, "needs_abstracts");
+ continue;
+ }
/* Looks okay. Just copy the data using Copy */
ci = Copy(k.item);
Setattr(nn, key, ci);
@@ -788,6 +793,22 @@ static List *pure_abstracts(Node *n) {
return abstracts;
}
+/* Recompute the "abstracts" attribute for the classes in instantiated templates, similarly to update_defaultargs() above. */
+static void update_abstracts(Node *n) {
+ for (; n; n = nextSibling(n)) {
+ Node* const child = firstChild(n);
+ if (!child)
+ continue;
+
+ update_abstracts(child);
+
+ if (Getattr(n, "needs_abstracts")) {
+ Setattr(n, "abstracts", pure_abstracts(child));
+ Delattr(n, "needs_abstracts");
+ }
+ }
+}
+
/* Make a classname */
static String *make_class_name(String *name) {
@@ -3056,6 +3077,7 @@ template_directive: SWIGTEMPLATE LPAREN idstringopt RPAREN idcolonnt LESSTHAN va
nn = Getattr(nn,"sym:nextSibling"); /* repeat for overloaded templated functions. If a templated class there will never be a sibling. */
}
update_defaultargs(linkliststart);
+ update_abstracts(linkliststart);
}
Swig_symbol_setscope(tscope);
Delete(Namespaceprefix);