Fix using declarations for deep inheritance hierarchies

Fixes inheritance hierarchies more than two deep and the using
declarations are overloaded. Using declarations
from a base class' base were not available for use in the target
language. For example in the code below, Using1::usingmethod(int i)
was not wrapped for use in Using3:

  struct Using1 {
  protected:
    void usingmethod(int i) {}
  };
  struct Using2 : Using1 {
  protected:
    void usingmethod(int i, int j) {}
    using Using1::usingmethod;
  };
  struct Using3 : Using2 {
    void usingmethod(int i, int j, int k) {}
    using Using2::usingmethod;
  };

Similarly for C++11 using declarations for inheriting constructors.
This commit is contained in:
William S Fulton 2023-08-04 20:01:43 +01:00
parent e41080216c
commit 93732bb195
11 changed files with 355 additions and 104 deletions

View File

@ -7,6 +7,29 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.2.0 (in progress)
===========================
2023-08-04: wsfulton
Fix using declarations for inheritance hierarchies more than
two deep and the using declarations are overloaded. Using declarations
from a base class' base were not available for use in the target
language. For example in the code below, Using1::usingmethod(int i)
was not wrapped for use in Using3:
struct Using1 {
protected:
void usingmethod(int i) {}
};
struct Using2 : Using1 {
protected:
void usingmethod(int i, int j) {}
using Using1::usingmethod;
};
struct Using3 : Using2 {
void usingmethod(int i, int j, int k) {}
using Using2::usingmethod;
};
Similarly for C++11 using declarations for inheriting constructors.
2023-08-02: wsfulton
https://sourceforge.net/p/swig/bugs/932/
Fix missing constructor generation due to abstract class test

View File

@ -568,6 +568,7 @@ CPP_TEST_CASES += \
using_extend \
using_inherit \
using_member \
using_member_multiple_inherit \
using_member_scopes \
using_namespace \
using_namespace_loop \

View File

@ -295,6 +295,43 @@ struct ProotDerived2f : ProotBase2 {
ProotDerived2f(int) {}
using ProotBase2::meth;
};
// Deeper inheritance chain
struct DeepBase1 {
virtual ~DeepBase1() {}
DeepBase1(int i) {}
};
struct DeepBase2 : DeepBase1 {
DeepBase2(int i, int j) : DeepBase1(i+j) {}
using DeepBase1::DeepBase1;
};
struct DeepBase3 : DeepBase2 {
DeepBase3(int i, int j, int k) : DeepBase2(i+j+k) {}
using DeepBase2::DeepBase2;
};
struct DeepProtectedBase1 {
virtual ~DeepProtectedBase1() {}
protected:
DeepProtectedBase1(int i) {}
};
struct DeepProtectedBase2 : DeepProtectedBase1 {
protected:
DeepProtectedBase2(int i, int j) : DeepProtectedBase1(i+j) {}
using DeepProtectedBase1::DeepProtectedBase1;
};
struct DeepProtectedBase3 : DeepProtectedBase2 {
DeepProtectedBase3(int i, int j, int k) : DeepProtectedBase2(i+j+k) {}
using DeepProtectedBase2::DeepProtectedBase2;
};
void cpptester() {
DeepBase3 db3 = DeepBase3(11);
db3 = DeepBase3(11, 22);
db3 = DeepBase3(11, 22, 33);
DeepProtectedBase3 dbp3 = DeepProtectedBase3(11, 22, 33);
}
%}
// Missing base

View File

@ -107,6 +107,12 @@ public class cpp11_director_using_constructor_runme {
new ProotDerived2f().meth();
new ProotDerived2f(0).meth();
// Deeper inheritance chain
DeepBase3 db3 = new DeepBase3(11);
db3 = new DeepBase3(11, 22);
db3 = new DeepBase3(11, 22, 33);
DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33);
// Missing base
// new HiddenDerived1();

View File

@ -105,6 +105,12 @@ public class cpp11_using_constructor_runme {
new ProotDerived2f().meth();
new ProotDerived2f(0).meth();
// Deeper inheritance chain
DeepBase3 db3 = new DeepBase3(11);
db3 = new DeepBase3(11, 22);
db3 = new DeepBase3(11, 22, 33);
DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33);
// Missing base
// new HiddenDerived1();

View File

@ -0,0 +1,33 @@
import using_member_multiple_inherit.*;
public class using_member_multiple_inherit_runme {
static {
try {
System.loadLibrary("using_member_multiple_inherit");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[])
{
// Single inheritance three deep, overload using and methods
Using3 u3 = new Using3();
u3.usingmethod(11);
u3.usingmethod(11, 22);
u3.usingmethod(11, 22, 33);
// Multiple inheritance, multiple using declarations
MultMiddleA ma = new MultMiddleA();
ma.multmethod(123);
ma.multmethod("hi");
ma.multmethod(123, 234);
MultBottomB mb = new MultBottomB();
mb.multmethod(123);
mb.multmethod("hi");
mb.multmethod(123, 234);
mb.multmethod(123, 345, 567);
}
}

View File

@ -93,6 +93,12 @@ ProotDerived2f(0, "hi").meth()
ProotDerived2f().meth()
ProotDerived2f(0).meth()
# Deeper inheritance chain
db3 = DeepBase3(11)
db3 = DeepBase3(11, 22)
db3 = DeepBase3(11, 22, 33)
dbp3 = DeepProtectedBase3(11, 22, 33)
# Missing base
# HiddenDerived1()

View File

@ -93,6 +93,12 @@ ProotDerived2f(0, "hi").meth()
ProotDerived2f().meth()
ProotDerived2f(0).meth()
# Deeper inheritance chain
db3 = DeepBase3(11)
db3 = DeepBase3(11, 22)
db3 = DeepBase3(11, 22, 33)
dbp3 = DeepProtectedBase3(11, 22, 33)
# Missing base
# HiddenDerived1()

View File

@ -0,0 +1,19 @@
from using_member_multiple_inherit import *
# Single inheritance three deep, overload using and methods
u3 = Using3()
u3.usingmethod(11)
u3.usingmethod(11, 22)
u3.usingmethod(11, 22, 33)
# Multiple inheritance, multiple using declarations
ma = MultMiddleA()
ma.multmethod(123)
ma.multmethod("hi")
ma.multmethod(123, 234)
mb = MultBottomB()
mb.multmethod(123)
mb.multmethod("hi")
mb.multmethod(123, 234)
mb.multmethod(123, 345, 567)

View File

@ -0,0 +1,82 @@
%module(ruby_minherit="1") using_member_multiple_inherit
%warnfilter(SWIGWARN_JAVA_MULTIPLE_INHERITANCE,
SWIGWARN_CSHARP_MULTIPLE_INHERITANCE,
SWIGWARN_D_MULTIPLE_INHERITANCE,
SWIGWARN_PHP_MULTIPLE_INHERITANCE) MultMiddleA; /* C#, D, Java, PHP multiple inheritance */
%warnfilter(SWIGWARN_JAVA_MULTIPLE_INHERITANCE,
SWIGWARN_CSHARP_MULTIPLE_INHERITANCE,
SWIGWARN_D_MULTIPLE_INHERITANCE,
SWIGWARN_PHP_MULTIPLE_INHERITANCE) MultMiddleB; /* C#, D, Java, PHP multiple inheritance */
%inline %{
// Single inheritance three deep, overload using and methods
struct Using1 {
protected:
void usingmethod(int i) {}
};
struct Using2 : Using1 {
protected:
void usingmethod(int i, int j) {}
using Using1::usingmethod;
};
struct Using3 : Using2 {
void usingmethod(int i, int j, int k) {}
using Using2::usingmethod;
};
// Multiple inheritance, multiple using declarations
struct Mult1 {
protected:
void multmethod(int i) {}
};
struct Mult2 {
protected:
void multmethod(const char *c) {}
};
struct MultMiddleA : Mult1, Mult2 {
public: // Note!
void multmethod(int i, int j) {}
using Mult1::multmethod;
using Mult2::multmethod;
};
#if !defined(SWIGD) // TODO: Fix bug adding incorrect override
struct MultBottomA : MultMiddleA {
void multmethod(int i, int j, int k) {}
using MultMiddleA::multmethod;
};
#endif
void cplusplus_testA() {
MultMiddleA m;
m.multmethod(123);
m.multmethod("hi");
m.multmethod(123, 234);
}
struct MultMiddleB : Mult1, Mult2 {
protected: // Note!
void multmethod(int i, int j) {}
using Mult1::multmethod;
using Mult2::multmethod;
};
struct MultBottomB : MultMiddleB {
void multmethod(int i, int j, int k) {}
using MultMiddleB::multmethod;
};
void cplusplus_testB() {
MultBottomB m;
m.multmethod(123);
m.multmethod("hi");
m.multmethod(123, 234);
m.multmethod(123, 345, 567);
}
/* TODO: fix when using declaration is declared before method, for example change MultMiddleA to:
struct MultMiddleB : Mult1, Mult2 {
protected: // Note!
using Mult1::multmethod;
using Mult2::multmethod;
void multmethod(int i, int j) {}
};
*/
%}

View File

@ -535,6 +535,136 @@ class Allocate:public Dispatcher {
}
}
/* -----------------------------------------------------------------------------
* clone_member_for_using_declaration()
*
* Create a new member (constructor or method) by copying it from member c, ready
* for adding as a child to the using declaration node n.
* ----------------------------------------------------------------------------- */
Node *clone_member_for_using_declaration(Node *c, Node *n) {
Node *parent = parentNode(n);
String *decl = Getattr(c, "decl");
String *symname = Getattr(n, "sym:name");
int match = 0;
Node *over = Getattr(n, "sym:overloaded");
while (over) {
String *odecl = Getattr(over, "decl");
if (Cmp(decl, odecl) == 0) {
match = 1;
break;
}
over = Getattr(over, "sym:nextSibling");
}
if (match) {
/* Don't generate a method if the method is overridden in this class,
* for example don't generate another m(bool) should there be a Base::m(bool) :
* struct Derived : Base {
* void m(bool);
* using Base::m;
* };
*/
return 0;
}
Node *nn = copyNode(c);
Setfile(nn, Getfile(n));
Setline(nn, Getline(n));
if (!Getattr(nn, "sym:name"))
Setattr(nn, "sym:name", symname);
Symtab *st = Getattr(n, "sym:symtab");
assert(st);
Setattr(nn, "sym:symtab", st);
// The real parent is the "using" declaration node, but subsequent code generally handles
// and expects a class member to point to the parent class node
Setattr(nn, "parentNode", parent);
if (Equal(nodeType(c), "constructor")) {
Setattr(nn, "name", Getattr(n, "name"));
Setattr(nn, "sym:name", Getattr(n, "sym:name"));
// Note that the added constructor's access is the same as that of
// the base class' constructor not of the using declaration.
// It has already been set correctly and should not be changed.
} else {
// Access might be different from the method in the base class
Delattr(nn, "access");
Setattr(nn, "access", Getattr(n, "access"));
}
if (!GetFlag(nn, "feature:ignore")) {
ParmList *parms = CopyParmList(Getattr(c, "parms"));
int is_pointer = SwigType_ispointer_return(Getattr(nn, "decl"));
int is_void = checkAttribute(nn, "type", "void") && !is_pointer;
Setattr(nn, "parms", parms);
Delete(parms);
if (Getattr(n, "feature:extend")) {
String *ucode = is_void ? NewStringf("{ self->%s(", Getattr(n, "uname")) : NewStringf("{ return self->%s(", Getattr(n, "uname"));
for (ParmList *p = parms; p;) {
Append(ucode, Getattr(p, "name"));
p = nextSibling(p);
if (p)
Append(ucode, ",");
}
Append(ucode, "); }");
Setattr(nn, "code", ucode);
Delete(ucode);
}
ParmList *throw_parm_list = Getattr(c, "throws");
if (throw_parm_list)
Setattr(nn, "throws", CopyParmList(throw_parm_list));
} else {
Delete(nn);
nn = 0;
}
return nn;
}
/* -----------------------------------------------------------------------------
* add_member_for_using_declaration()
*
* Add a new member (constructor or method) by copying it from member c.
* Add it into the linked list of members under the using declaration n (ccount,
* unodes and last_unodes are used for this).
* ----------------------------------------------------------------------------- */
void add_member_for_using_declaration(Node *c, Node *n, int &ccount, Node *&unodes, Node *&last_unodes) {
if (!(Swig_storage_isstatic(c)
|| checkAttribute(c, "storage", "typedef")
|| Strstr(Getattr(c, "storage"), "friend")
|| (Getattr(c, "feature:extend") && !Getattr(c, "code"))
|| GetFlag(c, "feature:ignore"))) {
String *symname = Getattr(n, "sym:name");
String *csymname = Getattr(c, "sym:name");
Node *parent = parentNode(n);
bool using_inherited_constructor_symname_okay = Equal(nodeType(c), "constructor") && Equal(symname, Getattr(parent, "sym:name"));
if (!csymname || Equal(csymname, symname) || using_inherited_constructor_symname_okay) {
Node *nn = clone_member_for_using_declaration(c, n);
if (nn) {
ccount++;
if (!last_unodes) {
last_unodes = nn;
unodes = nn;
} else {
Setattr(nn, "previousSibling", last_unodes);
Setattr(last_unodes, "nextSibling", nn);
Setattr(nn, "sym:previousSibling", last_unodes);
Setattr(last_unodes, "sym:nextSibling", nn);
Setattr(nn, "sym:overloaded", unodes);
Setattr(unodes, "sym:overloaded", unodes);
last_unodes = nn;
}
}
} else {
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(n), Getline(n), "Using declaration %s, with name '%s', is not actually using\n", SwigType_namestr(Getattr(n, "uname")), symname);
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(c), Getline(c), "the method from %s, with name '%s', as the names are different.\n", Swig_name_decl(c), csymname);
}
}
}
public:
Allocate():
inclass(NULL), extendmode(0) {
@ -824,118 +954,20 @@ Allocate():
} else {
String *ntype = nodeType(ns);
if (Equal(ntype, "cdecl") || Equal(ntype, "constructor")) {
if (!checkAttribute(ns, "storage", "typedef")) {
{
/* A normal C declaration or constructor declaration
* Now add a new class member top the parse tree (copied from the base class member pointed to by the using declaration) */
if ((inclass) && (!GetFlag(n, "feature:ignore")) && (Getattr(n, "sym:name"))) {
Node *c = ns;
Node *unodes = 0, *last_unodes = 0;
int ccount = 0;
String *symname = Getattr(n, "sym:name");
Node *parent = parentNode(n);
while (c) {
if (Strcmp(nodeType(c), ntype) == 0) {
if (!(Swig_storage_isstatic(c)
|| checkAttribute(c, "storage", "typedef")
|| Strstr(Getattr(c, "storage"), "friend")
|| (Getattr(c, "feature:extend") && !Getattr(c, "code"))
|| GetFlag(c, "feature:ignore"))) {
String *csymname = Getattr(c, "sym:name");
bool using_inherited_constructor_symname_okay = Equal(nodeType(c), "constructor") && Equal(symname, Getattr(parent, "sym:name"));
if (!csymname || Equal(csymname, symname) || using_inherited_constructor_symname_okay) {
String *decl = Getattr(c, "decl");
int match = 0;
Node *over = Getattr(n, "sym:overloaded");
while (over) {
String *odecl = Getattr(over, "decl");
if (Cmp(decl, odecl) == 0) {
match = 1;
break;
}
over = Getattr(over, "sym:nextSibling");
}
if (match) {
/* Don't generate a method if the method is overridden in this class,
* for example don't generate another m(bool) should there be a Base::m(bool) :
* struct Derived : Base {
* void m(bool);
* using Base::m;
* };
*/
c = Getattr(c, "csym:nextSibling");
continue;
}
Node *nn = copyNode(c);
Setfile(nn, Getfile(n));
Setline(nn, Getline(n));
if (!Getattr(nn, "sym:name"))
Setattr(nn, "sym:name", symname);
Symtab *st = Getattr(n, "sym:symtab");
assert(st);
Setattr(nn, "sym:symtab", st);
// The real parent is the "using" declaration node, but subsequent code generally handles
// and expects a class member to point to the parent class node
Setattr(nn, "parentNode", parent);
if (Equal(ntype, "constructor")) {
Setattr(nn, "name", Getattr(n, "name"));
Setattr(nn, "sym:name", Getattr(n, "sym:name"));
// Note that the added constructor's access is the same as that of
// the base class' constructor not of the using declaration.
// It has already been set correctly and should not be changed.
} else {
// Access might be different from the method in the base class
Delattr(nn, "access");
Setattr(nn, "access", Getattr(n, "access"));
}
if (!GetFlag(nn, "feature:ignore")) {
ParmList *parms = CopyParmList(Getattr(c, "parms"));
int is_pointer = SwigType_ispointer_return(Getattr(nn, "decl"));
int is_void = checkAttribute(nn, "type", "void") && !is_pointer;
Setattr(nn, "parms", parms);
Delete(parms);
if (Getattr(n, "feature:extend")) {
String *ucode = is_void ? NewStringf("{ self->%s(", Getattr(n, "uname")) : NewStringf("{ return self->%s(", Getattr(n, "uname"));
for (ParmList *p = parms; p;) {
Append(ucode, Getattr(p, "name"));
p = nextSibling(p);
if (p)
Append(ucode, ",");
}
Append(ucode, "); }");
Setattr(nn, "code", ucode);
Delete(ucode);
}
ParmList *throw_parm_list = Getattr(c, "throws");
if (throw_parm_list)
Setattr(nn, "throws", CopyParmList(throw_parm_list));
ccount++;
if (!last_unodes) {
last_unodes = nn;
unodes = nn;
} else {
Setattr(nn, "previousSibling", last_unodes);
Setattr(last_unodes, "nextSibling", nn);
Setattr(nn, "sym:previousSibling", last_unodes);
Setattr(last_unodes, "sym:nextSibling", nn);
Setattr(nn, "sym:overloaded", unodes);
Setattr(unodes, "sym:overloaded", unodes);
last_unodes = nn;
}
} else {
Delete(nn);
}
} else {
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(n), Getline(n), "Using declaration %s, with name '%s', is not actually using\n", SwigType_namestr(Getattr(n, "uname")), symname);
Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(c), Getline(c), "the method from %s, with name '%s', as the names are different.\n", Swig_name_decl(c), csymname);
}
if (Equal(nodeType(c), ntype)) {
add_member_for_using_declaration(c, n, ccount, unodes, last_unodes);
} else if (Equal(nodeType(c), "using")) {
for (Node *member = firstChild(c); member; member = nextSibling(member)) {
add_member_for_using_declaration(member, n, ccount, unodes, last_unodes);
}
}
c = Getattr(c, "csym:nextSibling");