Menu

#1367 Crash when trying to define an instance of a template class that inherits from a specialized template instance of itself.

open
nobody
None
5
2023-08-07
2014-04-10
Lee Wilson
No

Using SWIG 3.0.0, I am trying to get SWIG to handle (or at least not crash) when I try to define a template of a class that inherits from a specialized template instance of itself. Here is a toy program to illustrate the issue:

template <typename T, int N = 0> class A;

template <typename T> class A<T, 0>
{
public:
    A() : mFoo(NULL)    {}
    virtual ~A()        {}

    T* getFoo() { return mFoo; }

protected:
    T* mFoo;
};

template <typename T, int N = 0> class A : public A<T, 0>
{
public:
    A() : A<T, 0>(), mBar(N)    {}
    virtual ~A()                {}

    int getBar() const { return mBar; }

protected:
    int mBar;
};

then in the .i file, I try

%template(tA) A<char, 0>;  // causes SWIG to crash

or

%template(tABase) A<char, 0>;  // causes SWIG to crash
%template(tA) A<char>;

or

%template() A<char, 0>;    // doesn't crash (yay!)
%template(tA) A<char>;     // tA ends up with only the methods of T<A, N> and does NOT inherit from T<A, 0> (poop!)

A more thorough description of the problem is also at http://stackoverflow.com/questions/23000390/handling-class-inheritance-from-specialized-instance-of-self-in-swig

Discussion

  • William Fulton

    William Fulton - 2014-04-15

    A C++ compiler doesn't like your code, g++ gives:

    example_wrap.cxx:216:40: error: redefinition of default argument for ‘int N’
    template <typename T,="" int="" N="0"> class A : public A<T, 0="">
    ^
    example_wrap.cxx:214:31: note: original definition appeared here
    template <typename T,="" int="" N="0"> class A;
    ^
    If you change:

    template <typename T, int N = 0> class A;
    

    to

    template <typename T, int N> class A;
    

    and put the specialization after the full template declaration then

    %template(tA) A<char, 0>;
    

    will work. There are of course still problems with SWIG, but you at least have a workaround.

     
  • Olly Betts

    Olly Betts - 2022-03-20

    The original invalid input still causes a crash with SWIG git master, but there is now a warning:

    test.i:2: Warning 323: Recursive scope inheritance of 'A< char,0 >'.
    Segmentation fault
    

    Just removing the = 0 as suggested above gives an example with GCC and clang seem to think is valid, but SWIG still crashes (which seems worse than a crash on invalid C++):

    test.i:2: Warning 323: Recursive scope inheritance of 'A< char,0 >'.
    Segmentation fault
    
     
  • Olly Betts

    Olly Betts - 2023-08-07

    Current git master (2e1506c1896f90456e5b3092ba7c7200c1b1e2e1) now emits two errors, but then still segfaults (with both the original and with = 0 removed to make it valid C++):

     ../preinst-swig -c++ -python test.i
    test.i:2: Warning 323: Recursive scope inheritance of 'A<(char,0)>'.
    test.i:2: Warning 323: Recursive scope inheritance of 'A< char,0 >'.
    Segmentation fault (core dumped)
    

    The crash is due to an infinite recursion, which this patch fixes:

    diff --git a/Source/Modules/allocate.cxx b/Source/Modules/allocate.cxx
    index 004036c2..25c77639 100644
    --- a/Source/Modules/allocate.cxx
    +++ b/Source/Modules/allocate.cxx
    @@ -125,7 +125,8 @@ class Allocate:public Dispatcher {
         resolved_decl = 0;
         for (int j = 0; j < Len(bases); j++) {
           Node *b = Getitem(bases, j);
    -      if (function_is_defined_in_bases(n, Getattr(b, "allbases")))
    +      Node *allbases = Getattr(b, "allbases");
    +      if (allbases != bases && function_is_defined_in_bases(n, allbases))
            return 1;
         }
         return 0;
    

    This change just results in a segfault later on (I can tell it's later because the crash above is before SWIG complains if no module name is specified but the new crash is after that).

    The new crash is fixed by this:

    diff --git a/Source/Swig/cwrap.c b/Source/Swig/cwrap.c
    index 2da9e478..ed90c82e 100644
    --- a/Source/Swig/cwrap.c
    +++ b/Source/Swig/cwrap.c
    @@ -665,12 +665,14 @@ static String *recursive_flag_search(Node *n, const String *attr, const String *
         if (bl) {
           Iterator bi;
           for (bi = First(bl); bi.item; bi = Next(bi)) {
    -       f = recursive_flag_search(bi.item, attr, noattr);
    -       if (f) {
    +       if (bi.item != n) {
    +         f = recursive_flag_search(bi.item, attr, noattr);
    +         if (f) {
     #ifdef SWIG_FAST_REC_SEARCH
    -         SetFlagAttr(n, attr, f);
    +           SetFlagAttr(n, attr, f);
     #endif
    -         return f;
    +           return f;
    +         }
            }
           }
         }
    

    With both patches SWIG completes without crashing!

    However these patches seem like they are papering over the problem - it'll still be there for other code to fall into. It seems to me we generally should not be creating structures which contain themselves like this (certainly in the first case as a class can't be a base class of itself - I'm less clear what the second structure actually is). I don't think I understand this area of SWIG enough to easily find where these get built though. @wsfulton Ideas?

     

Log in to post a comment.