%template scope enforcement and class definition fixes

The scoping rules around %template have been specified and enforced.
The %template directive for a class template is the equivalent to an
explicit instantiation of a C++ class template. The scope for a valid
%template instantiation is now the same as the scope required for a
valid explicit instantiation of a C++ template. A definition of the
template for the explicit instantiation must be in scope where the
instantiation is declared and must not be enclosed within a different
namespace.

For example, a few %template and explicit instantiations of std::vector
are shown below:

  // valid
  namespace std {
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // valid
  using namespace std;
  %template(vin) vector<int>;
  template class vector<int>;

  // valid
  using std::vector;
  %template(vin) vector<int>;
  template class vector<int>;

  // ill-formed
  namespace unrelated {
    using std::vector;
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // ill-formed
  namespace unrelated {
    using namespace std;
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // ill-formed
  namespace unrelated {
    namespace std {
      %template(vin) vector<int>;
      template class vector<int>;
    }
  }

  // ill-formed
  namespace unrelated {
    %template(vin) std::vector<int>;
    template class std::vector<int>;
  }

When the scope is incorrect, an error now occurs such as:

cpp_template_scope.i:34: Error: 'vector' resolves to 'std::vector' and
was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.

Previously SWIG accepted the ill-formed examples above but this led to
numerous subtle template scope problems especially in the presence of
using declarations and using directives as well as with %feature and %typemap.

Actually, a valid instantiation is one which conforms to the C++03
standard as C++11 made a change to disallow using declarations and
using directives to find a template.

  // valid C++03, ill-formed C++11
  using std::vector;
  template class vector<int>;

Similar fixes for defining classes using forward class references have
also been put in place. For example:

namespace Space1 {
  struct A;
}
namespace Space2 {
  struct Space1::A {
    void x();
  }
}

will now error out with:

cpp_class_definition.i:5: Error: 'Space1::A' resolves to 'Space1::A' and
was incorrectly instantiated in scope 'Space2' instead of within scope 'Space1'.
This commit is contained in:
William S Fulton 2017-08-09 00:06:24 +01:00
commit 959e627208
21 changed files with 689 additions and 114 deletions

View file

@ -0,0 +1,153 @@
// Test a mix of forward class declarations, class definitions, using declarations and using directives.
%module class_scope_namespace
%warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) H::HH;
%warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) Space8::I::II;
%inline %{
struct A;
namespace Space1 {
namespace SubSpace1 {
struct A {
void aa(Space1::SubSpace1::A, SubSpace1::A, A) {}
};
void aaa(Space1::SubSpace1::A, SubSpace1::A, A) {}
}
}
namespace Space2 {
struct B;
}
using Space2::B;
struct B {
void bb(Space2::B, B) {}
};
void bbb(Space2::B, B) {}
namespace Space3 {
namespace SubSpace3 {
struct C;
struct D;
}
}
struct C;
struct D;
namespace Space3 {
struct C;
struct SubSpace3::C {
void cc(Space3::SubSpace3::C, SubSpace3::C) {}
};
using SubSpace3::D;
struct SubSpace3::D {
void dd(Space3::SubSpace3::D, SubSpace3::D, D) {}
};
void ccc(Space3::SubSpace3::C, SubSpace3::C) {}
void ddd(Space3::SubSpace3::D, SubSpace3::D, D) {}
}
namespace Space4 {
namespace SubSpace4 {
struct E;
}
}
using namespace Space4;
using SubSpace4::E;
// Was added to incorrect namespace in swig-3.0.12
struct SubSpace4::E {
void ee(Space4::SubSpace4::E, SubSpace4::E, E) {}
};
void eee(Space4::SubSpace4::E, SubSpace4::E, E) {}
namespace Space5 {
namespace SubSpace5 {
namespace SubSubSpace5 {
struct F;
}
}
}
namespace Space5 {
using namespace SubSpace5;
using SubSubSpace5::F;
// Was added to incorrect namespace in swig-3.0.12
struct SubSubSpace5::F {
void ff(Space5::SubSpace5::SubSubSpace5::F, SubSpace5::SubSubSpace5::F, SubSubSpace5::F, F) {}
};
// needs fixing
void fff(Space5::SubSpace5::SubSubSpace5::F, SubSpace5::SubSubSpace5::F, /*SubSubSpace5::F,*/ F) {}
}
namespace Space6 {
struct G;
namespace SubSpace6 {
struct G;
}
}
namespace Space6 {
struct SubSpace6::G {
void gg(Space6::SubSpace6::G, SubSpace6::G) {}
};
void ggg(Space6::SubSpace6::G, SubSpace6::G) {}
}
struct HH;
struct H {
struct HH {
void hh(H::HH) {}
};
};
void hhh(H::HH) {}
namespace Space8 {
struct II;
struct I {
struct II {
void ii(Space8::I::II, I::II) {}
};
};
void iii(Space8::I::II, I::II) {}
}
struct J;
namespace Space9 {
namespace SubSpace9 {
struct J {
void jj(Space9::SubSpace9::J, SubSpace9::J, J) {}
};
void jjj(Space9::SubSpace9::J, SubSpace9::J, J) {}
}
}
namespace Space10 {
struct K;
}
namespace Space10 {
namespace SubSpace10 {
struct K {
void kk(Space10::SubSpace10::K, SubSpace10::K, K) {}
};
void kkk(Space10::SubSpace10::K, SubSpace10::K, K) {}
}
}
namespace OtherSpace {
struct L;
struct M;
}
using OtherSpace::L;
namespace Space11 {
using OtherSpace::M;
namespace SubSpace11 {
struct L {
void ll(Space11::SubSpace11::L, SubSpace11::L, L) {}
};
void lll(Space11::SubSpace11::L, SubSpace11::L, L) {}
struct M {
void mm(Space11::SubSpace11::M, SubSpace11::M, M) {}
};
void mmm(Space11::SubSpace11::M, SubSpace11::M, M) {}
}
}
%}

View file

@ -136,6 +136,7 @@ CPP_TEST_CASES += \
char_binary \
char_strings \
chartest \
class_scope_namespace \
class_forward \
class_ignore \
class_scope_weird \

View file

@ -0,0 +1,26 @@
%module xxx
// This should error but doesn't
#if 0
namespace OtherSpace {
struct L;
}
namespace Space11 {
namespace SubSpace11 {
using OtherSpace::L;
struct L {
void ll();
};
}
}
#endif
namespace Space1 {
struct A;
}
namespace Space2 {
struct Space1::A {
void x();
};
}

View file

@ -0,0 +1 @@
cpp_class_definition.i:22: Error: 'Space1::A' resolves to 'Space1::A' and was incorrectly instantiated in scope 'Space2' instead of within scope 'Space1'.

View file

@ -0,0 +1,40 @@
%module namespace_template
namespace test {
template<typename T> T max(T a, T b) { return (a > b) ? a : b; }
template<typename T> class vector {
public:
vector() { }
~vector() { }
};
}
namespace test2 {
using namespace test;
%template(maxshort) max<short>;
%template(vectorshort) vector<short>;
}
namespace test3 {
using test::max;
using test::vector;
%template(maxlong) max<long>;
%template(vectorlong) vector<long>;
}
namespace test4 {
using namespace test;
typedef int Integer;
}
namespace test4 {
%template(maxInteger) max<Integer>;
%template(vectorInteger) vector<Integer>;
}
using namespace test;
namespace test5 {
%template(maxdouble) max<double>;
%template(vectordouble) vector<double>;
}

View file

@ -0,0 +1,9 @@
cpp_namespace_template_bad.i:14: Error: 'max' resolves to 'test::max' and was incorrectly instantiated in scope 'test2' instead of within scope 'test'.
cpp_namespace_template_bad.i:15: Error: 'vector' resolves to 'test::vector' and was incorrectly instantiated in scope 'test2' instead of within scope 'test'.
cpp_namespace_template_bad.i:21: Error: 'max' resolves to 'test::max' and was incorrectly instantiated in scope 'test3' instead of within scope 'test'.
cpp_namespace_template_bad.i:22: Error: 'vector' resolves to 'test::vector' and was incorrectly instantiated in scope 'test3' instead of within scope 'test'.
cpp_namespace_template_bad.i:31: Error: 'max' resolves to 'test::max' and was incorrectly instantiated in scope 'test4' instead of within scope 'test'.
cpp_namespace_template_bad.i:32: Error: 'vector' resolves to 'test::vector' and was incorrectly instantiated in scope 'test4' instead of within scope 'test'.
cpp_namespace_template_bad.i:37: Error: 'max' resolves to 'test::max' and was incorrectly instantiated in scope 'test5' instead of within scope 'test'.
cpp_namespace_template_bad.i:37: Error: Template 'max' undefined.
cpp_namespace_template_bad.i:38: Error: 'vector' resolves to 'test::vector' and was incorrectly instantiated in scope 'test5' instead of within scope 'test'.

View file

@ -1,2 +1,4 @@
cpp_nested_template.i:9: Error: 'Temply' resolves to '::Temply' and was incorrectly instantiated in scope 'A' instead of within scope ''.
cpp_nested_template.i:9: Warning 324: Named nested template instantiations not supported. Processing as if no name was given to %template().
cpp_nested_template.i:18: Error: 'Temply' resolves to '::Temply' and was incorrectly instantiated in scope 'B' instead of within scope ''.
cpp_nested_template.i:18: Warning 324: Named nested template instantiations not supported. Processing as if no name was given to %template().

View file

@ -0,0 +1,57 @@
%module xxx
namespace std {
template<typename T> class vector {};
}
struct S1 {};
struct S2 {};
struct S3 {};
struct S4 {};
struct S5 {};
struct S6 {};
struct S7 {};
// valid
namespace std {
%template(vi1) vector<S1>;
template class vector<S1>;
}
// valid
using namespace std;
%template(vi2) vector<S2>;
template class vector<S2>;
// valid
using std::vector;
%template(vi3) vector<S3>;
template class vector<S3>;
// ill-formed
namespace unrelated {
using std::vector;
%template(vi4) vector<S4>;
template class vector<S4>;
}
// ill-formed
namespace unrelated {
using namespace std;
%template(vi5) vector<S5>;
template class vector<S5>;
}
// ill-formed
namespace unrelated {
namespace std {
%template(vi6) vector<S6>;
template class vector<S6>;
}
}
// ill-formed
namespace unrelated {
%template(vi7) std::vector<S7>;
template class std::vector<S7>;
}

View file

@ -0,0 +1,11 @@
cpp_template_scope.i:18: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:24: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:29: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:34: Error: 'vector' resolves to 'std::vector' and was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.
cpp_template_scope.i:35: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:41: Error: 'vector' resolves to 'std::vector' and was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.
cpp_template_scope.i:42: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:48: Error: 'vector' resolves to 'std::vector' and was incorrectly instantiated in scope 'unrelated::std' instead of within scope 'std'.
cpp_template_scope.i:49: Warning 320: Explicit template instantiation ignored.
cpp_template_scope.i:55: Error: 'std::vector' resolves to 'std::vector' and was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.
cpp_template_scope.i:56: Warning 320: Explicit template instantiation ignored.

View file

@ -0,0 +1,59 @@
import class_scope_namespace.*;
public class class_scope_namespace_runme {
static {
try {
System.loadLibrary("class_scope_namespace");
} 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[])
{
A a = new A();
B b = new B();
C c = new C();
D d = new D();
E e = new E();
F f = new F();
G g = new G();
H.HH h = new H.HH();
I.II i = new I.II();
J j = new J();
K k = new K();
L l = new L();
M m = new M();
a.aa(a, a, a);
b.bb(b, b);
c.cc(c, c);
d.dd(d, d, d);
e.ee(e, e, e);
f.ff(f, f, f, f);
g.gg(g, g);
h.hh(h);
i.ii(i, i);
j.jj(j, j, j);
k.kk(k, k, k);
l.ll(l, l, l);
m.mm(m, m, m);
class_scope_namespace.aaa(a, a, a);
class_scope_namespace.bbb(b, b);
class_scope_namespace.ccc(c, c);
class_scope_namespace.ddd(d, d, d);
class_scope_namespace.eee(e, e, e);
class_scope_namespace.fff(f, f, f);
class_scope_namespace.ggg(g, g);
class_scope_namespace.hhh(h);
class_scope_namespace.iii(i, i);
class_scope_namespace.jjj(j, j, j);
class_scope_namespace.kkk(k, k, k);
class_scope_namespace.lll(l, l, l);
class_scope_namespace.mmm(m, m, m);
}
}

View file

@ -0,0 +1,32 @@
import namespace_template.*;
public class namespace_template_runme {
static {
try {
System.loadLibrary("namespace_template");
} 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[]) {
vectorchar vc = new vectorchar();
vectorshort vs = new vectorshort();
vectorint vi = new vectorint();
vectorlong vl = new vectorlong();
vc.blah((char)10);
vs.blah((short)10);
vi.blah(10);
vl.blah(10);
vc.vectoruse(vc, vc);
vs.vectoruse(vs, vs);
vi.vectoruse(vi, vi);
vl.vectoruse(vl, vl);
}
}

View file

@ -2,10 +2,10 @@
%module namespace_template
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) vector<int>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test2::vector<short>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test3::vector<long>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) vector<test4::Integer>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test::vector<int>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test::vector<short>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test::vector<long>; /* Ruby, wrong class name */
%warnfilter(SWIGWARN_RUBY_WRONG_NAME) test::vector<test::Char>; /* Ruby, wrong class name */
%{
#ifdef max
@ -23,20 +23,9 @@ namespace test {
char * blah(T x) {
return (char *) "vector::blah";
}
void vectoruse(vector<T> a, test::vector<T> b) {}
};
}
namespace test2 {
using namespace test;
}
namespace test3 {
using test::max;
using test::vector;
}
using namespace test2;
namespace T4 = test;
%}
namespace test {
@ -48,6 +37,7 @@ namespace test {
char * blah(T x) {
return (char *) "vector::blah";
}
void vectoruse(vector<T> a, test::vector<T> b) {}
};
}
@ -55,30 +45,26 @@ using namespace test;
%template(maxint) max<int>;
%template(vectorint) vector<int>;
namespace test2 {
using namespace test;
namespace test {
%template(maxshort) max<short>;
%template(vectorshort) vector<short>;
}
namespace test3 {
using test::max;
using test::vector;
namespace test {
%template(maxlong) max<long>;
%template(vectorlong) vector<long>;
}
%inline %{
namespace test4 {
using namespace test;
typedef int Integer;
namespace test {
typedef char Char;
}
%}
namespace test4 {
%template(maxInteger) max<Integer>;
%template(vectorInteger) vector<Integer>;
namespace test {
%template(maxchar) max<Char>;
%template(vectorchar) vector<Char>;
}

View file

@ -49,11 +49,6 @@ namespace one
};
}
%define PTR_DEF(o)
typedef one::Ptr<o> o ## _ptr;
%template(o ## _ptr) one::Ptr<o>;
%enddef
namespace one
{
class Obj1
@ -63,7 +58,8 @@ namespace one
void donothing() {}
};
PTR_DEF(Obj1)
typedef one::Ptr<Obj1> Obj1_ptr;
%template(Obj1_ptr) one::Ptr<Obj1>;
}
namespace two
@ -75,6 +71,9 @@ namespace two
void donothing() {}
};
PTR_DEF(Obj2)
typedef one::Ptr<Obj2> Obj2_ptr;
}
using two::Obj2;
%template(Obj2_ptr) one::Ptr<Obj2>;

View file

@ -1,25 +1,30 @@
#pragma SWIG nowarn=SWIGWARN_PARSE_NESTED_TEMPLATE
%module template_nested_typemaps
// Testing that the typemaps invoked within a class via %template are picked up by appropriate methods
#pragma SWIG nowarn=SWIGWARN_PARSE_NAMED_NESTED_CLASS
template <typename T> struct Typemap {
%typemap(in) T {
$1 = -99;
}
};
template <> struct Typemap<short> { // Note explicit specialization
%typemap(in) short {
$1 = -77;
}
};
// Testing that the typemaps invoked within a class via %template are picked up by appropriate methods
// Only for languages that support nested classes
%inline %{
int globalInt1(int s) { return s; }
short globalShort1(short s) { return s; }
template <typename T> struct Breeze {
template <typename TMT> struct Typemap {
#ifdef SWIG
%typemap(in) TMT {
$1 = -99;
}
#endif
};
template <typename TMT> struct TypemapShort {
#ifdef SWIG
%typemap(in) short {
$1 = -77;
}
#endif
};
int methodInt1(int s) { return s; }
#if defined(SWIG)
%template() Typemap<int>;
@ -29,7 +34,7 @@ template <typename T> struct Breeze {
short methodShort1(short s) { return s; }
#if defined(SWIG)
%template(TypemapShort) Typemap<short>; // should issue warning SWIGWARN_PARSE_NESTED_TEMPLATE
%template() TypemapShort<short>;
#endif
short methodShort2(short s) { return s; } // should pick up the typemap within Typemap<short>
};

View file

@ -32,7 +32,7 @@ namespace One {
%template(H) One::OneParm<int **>;
// %template scope explicit specializations
namespace ONE {
namespace One {
%template(I) One::OneParm<float>;
%template(J) ::One::OneParm<float *>;
}
@ -42,7 +42,7 @@ namespace One {
}
// %template scope partial specializations
namespace ONE {
namespace One {
%template(BB) One::OneParm<bool *>;
%template(BBB) ::One::OneParm<char *>;
}

View file

@ -59,7 +59,7 @@ namespace One {
%template(H) One::OneParm<TypeDef::IntPtrPtr>;
// %template scope explicit specializations
namespace ONE {
namespace One {
%template(I) One::OneParm<TypeDef::Float>;
%template(J) ::One::OneParm<TypeDef::FloatPtr>;
}
@ -69,7 +69,7 @@ namespace One {
}
// %template scope partial specializations
namespace ONE {
namespace One {
%template(BB) One::OneParm<TypeDef::BoolPtr>;
%template(BBB) ::One::OneParm<TypeDef::CharPtr>;
}