diff --git a/CHANGES.current b/CHANGES.current index c8f71837a..2b77e63f3 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -47,6 +47,9 @@ Version 4.0.0 (in progress) 2019-02-22: tamuratak #984 Add support for RTypedData introduced in Ruby 1.9.3. +2019-02-22: ZackerySpytz + #1483 Fix compilation failures when a director class has final methods. + 2019-02-21: wsfulton #1240 Suppress Java 9 deprecation warnings on finalize method. diff --git a/Doc/Manual/Go.html b/Doc/Manual/Go.html index 4a60e45e0..047b46edf 100644 --- a/Doc/Manual/Go.html +++ b/Doc/Manual/Go.html @@ -785,7 +785,9 @@ In order to override virtual methods on a C++ class with Go methods the NewDirectorClassName constructor functions receives a DirectorInterface argument. The methods in the DirectorInterface are a subset of the public and protected virtual methods -of the C++ class. If the DirectorInterface contains a method with a +of the C++ class. +Virtual methods that have a final specifier are unsurprisingly excluded. +If the DirectorInterface contains a method with a matching signature to a virtual method of the C++ class then the virtual C++ method will be overwritten with the Go method. As Go doesn't support protected methods all overridden protected virtual C++ methods will be public in Go. diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index bf77c1562..3a04b8a79 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -3629,7 +3629,7 @@ The %feature directive can be applied globally, to specific classes, and to spec // generate directors for all classes that have virtual methods %feature("director"); -// generate directors for all virtual methods in class Foo +// generate directors for the virtual methods in class Foo %feature("director") Foo; @@ -3647,7 +3647,7 @@ So for example,

-will generate directors for all virtual methods of class Foo except bar(). +will generate directors for the virtual methods of class Foo except bar().

@@ -3683,7 +3683,8 @@ The director classes store a pointer to their underlying Java proxy classes.

For simplicity let's ignore the Swig::Director class and refer to the original C++ class as the director's base class. By default, a director class extends all virtual methods in the inheritance chain of its base class (see the preceding section for how to modify this behavior). -Thus all virtual method calls, whether they originate in C++ or in Java via proxy classes, eventually end up in at the implementation in the director class. +Virtual methods that have a final specifier are unsurprisingly excluded. +Thus the virtual method calls, whether they originate in C++ or in Java via proxy classes, eventually end up in at the implementation in the director class. The job of the director methods is to route these method calls to the appropriate place in the inheritance chain. By "appropriate place" we mean the method that would have been called if the C++ base class and its Java derived classes were seamlessly integrated. That seamless integration is exactly what the director classes provide, transparently skipping over all the messy JNI glue code that binds the two languages together. diff --git a/Doc/Manual/Perl5.html b/Doc/Manual/Perl5.html index 5ce765c26..766ccaede 100644 --- a/Doc/Manual/Perl5.html +++ b/Doc/Manual/Perl5.html @@ -3070,7 +3070,7 @@ globally, to specific classes, and to specific methods, like this: // generate directors for all classes that have virtual methods %feature("director"); -// generate directors for all virtual methods in class Foo +// generate directors for the virtual methods in class Foo %feature("director") Foo; @@ -3088,7 +3088,7 @@ directors for specific classes or methods. So for example,

-will generate directors for all virtual methods of class Foo except +will generate directors for the virtual methods of class Foo except bar().

@@ -3153,7 +3153,8 @@ For simplicity let's ignore the Swig::Director class and refer to the original C++ class as the director's base class. By default, a director class extends all virtual methods in the inheritance chain of its base class (see the preceding section for how to modify this behavior). -Thus all virtual method calls, whether they originate in C++ or in +Virtual methods that have a final specifier are unsurprisingly excluded. +Thus the virtual method calls, whether they originate in C++ or in Perl via proxy classes, eventually end up in at the implementation in the director class. The job of the director methods is to route these method calls to the appropriate place in the inheritance chain. By diff --git a/Doc/Manual/Php.html b/Doc/Manual/Php.html index 85732fd33..1b8b2d468 100644 --- a/Doc/Manual/Php.html +++ b/Doc/Manual/Php.html @@ -937,7 +937,7 @@ globally, to specific classes, and to specific methods, like this: // generate directors for all classes that have virtual methods %feature("director"); -// generate directors for all virtual methods in class Foo +// generate directors for the virtual methods in class Foo %feature("director") Foo; @@ -955,7 +955,7 @@ directors for specific classes or methods. So for example,

-will generate directors for all virtual methods of class Foo except +will generate directors for the virtual methods of class Foo except bar().

@@ -1020,7 +1020,8 @@ For simplicity let's ignore the Swig::Director class and refer to the original C++ class as the director's base class. By default, a director class extends all virtual methods in the inheritance chain of its base class (see the preceding section for how to modify this behavior). -Thus all virtual method calls, whether they originate in C++ or in +Virtual methods that have a final specifier are unsurprisingly excluded. +Thus the virtual method calls, whether they originate in C++ or in PHP via proxy classes, eventually end up in at the implementation in the director class. The job of the director methods is to route these method calls to the appropriate place in the inheritance chain. By "appropriate diff --git a/Doc/Manual/Python.html b/Doc/Manual/Python.html index 6a174fddf..2554e9da3 100644 --- a/Doc/Manual/Python.html +++ b/Doc/Manual/Python.html @@ -2934,7 +2934,7 @@ globally, to specific classes, and to specific methods, like this: // generate directors for all classes that have virtual methods %feature("director"); -// generate directors for all virtual methods in class Foo +// generate directors for the virtual methods in class Foo %feature("director") Foo; @@ -2952,7 +2952,7 @@ directors for specific classes or methods. So for example,

-will generate directors for all virtual methods of class Foo except +will generate directors for the virtual methods of class Foo except bar().

@@ -3018,7 +3018,8 @@ For simplicity let's ignore the Swig::Director class and refer to the original C++ class as the director's base class. By default, a director class extends all virtual methods in the inheritance chain of its base class (see the preceding section for how to modify this behavior). -Thus all virtual method calls, whether they originate in C++ or in +Virtual methods that have a final specifier are unsurprisingly excluded. +Thus the virtual method calls, whether they originate in C++ or in Python via proxy classes, eventually end up in at the implementation in the director class. The job of the director methods is to route these method calls to the appropriate place in the inheritance chain. By diff --git a/Doc/Manual/Warnings.html b/Doc/Manual/Warnings.html index bff20801e..c63d7de0c 100644 --- a/Doc/Manual/Warnings.html +++ b/Doc/Manual/Warnings.html @@ -535,6 +535,7 @@ example.i(4) : Syntax error in input(1).
  • 522. Use of an illegal constructor name 'name' in %extend is deprecated, the constructor name should be 'name'.
  • 523. Use of an illegal destructor name 'name' in %extend is deprecated, the destructor name should be 'name'.
  • 524. Experimental target language. Target language language specified by lang is an experimental language. Please read about SWIG experimental languages, htmllink. +
  • 525. Destructor declaration is final, name cannot be a director class.

    18.9.6 Doxygen comments (560-599)

    diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 60f6a9522..67a63287d 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -572,6 +572,7 @@ CPP11_TEST_CASES += \ cpp11_director_enums \ cpp11_directors \ cpp11_explicit_conversion_operators \ + cpp11_final_directors \ cpp11_final_override \ cpp11_function_objects \ cpp11_inheriting_constructors \ diff --git a/Examples/test-suite/cpp11_final_directors.i b/Examples/test-suite/cpp11_final_directors.i new file mode 100644 index 000000000..b58111adb --- /dev/null +++ b/Examples/test-suite/cpp11_final_directors.i @@ -0,0 +1,33 @@ +%module(directors="1") cpp11_final_directors + +%director Derived; + +// Check SWIG will not wrap these classes as directors where the destructors are final +%director BaseFinalDestructor; +%director BaseFinalDestructor2; + +%warnfilter(SWIGWARN_LANG_DIRECTOR_FINAL) BaseFinalDestructor::~BaseFinalDestructor; +%warnfilter(SWIGWARN_LANG_DIRECTOR_FINAL) BaseFinalDestructor2::~BaseFinalDestructor2; + +%inline %{ +struct Base { + virtual void basemeth() final {} + virtual ~Base() {} +}; + +struct Derived : Base { + virtual int derivedmeth() final { return 1; } + virtual int meth() { return 2; } + virtual ~Derived() {} +}; + +struct BaseFinalDestructor { + virtual void basefinalmeth() final {} + virtual ~BaseFinalDestructor() final {} +}; + +struct BaseFinalDestructor2 { + virtual void basefinalmeth() {} + virtual ~BaseFinalDestructor2() final {} +}; +%} diff --git a/Examples/test-suite/errors/cpp_final_destructor.stderr b/Examples/test-suite/errors/cpp_final_destructor.stderr new file mode 100644 index 000000000..d8c509d0c --- /dev/null +++ b/Examples/test-suite/errors/cpp_final_destructor.stderr @@ -0,0 +1 @@ +cpp_final_destructor.i:7: Warning 525: Destructor BaseFinal::~BaseFinal() is final, BaseFinal cannot be a director class. diff --git a/Examples/test-suite/python/cpp11_final_directors_runme.py b/Examples/test-suite/python/cpp11_final_directors_runme.py new file mode 100644 index 000000000..2e5f8af96 --- /dev/null +++ b/Examples/test-suite/python/cpp11_final_directors_runme.py @@ -0,0 +1,11 @@ +import cpp11_final_directors + +class Derived2(cpp11_final_directors.Derived): + + def meth(self): + return 3 + + +b = Derived2() +if b.meth() != 3: + raise RuntimeError diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index f20f1db2f..4046e480e 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1552,6 +1552,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) Parm *throws; String *throwf; String *nexcept; + String *final; } dtype; struct { const char *type; @@ -1567,6 +1568,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) ParmList *throws; String *throwf; String *nexcept; + String *final; } decl; Parm *tparms; struct { @@ -3189,6 +3191,7 @@ c_decl : storage_class type declarator cpp_const initializer c_decl_tail { Setattr($$,"throws",$4.throws); Setattr($$,"throw",$4.throwf); Setattr($$,"noexcept",$4.nexcept); + Setattr($$,"final",$4.final); if ($5.val && $5.type) { /* store initializer type as it might be different to the declared type */ SwigType *valuetype = NewSwigType($5.type); @@ -3266,6 +3269,7 @@ c_decl : storage_class type declarator cpp_const initializer c_decl_tail { Setattr($$,"throws",$4.throws); Setattr($$,"throw",$4.throwf); Setattr($$,"noexcept",$4.nexcept); + Setattr($$,"final",$4.final); if (!$9) { if (Len(scanner_ccode)) { String *code = Copy(scanner_ccode); @@ -3330,6 +3334,7 @@ c_decl_tail : SEMI { Setattr($$,"throws",$3.throws); Setattr($$,"throw",$3.throwf); Setattr($$,"noexcept",$3.nexcept); + Setattr($$,"final",$3.final); if ($4.bitfield) { Setattr($$,"bitfield", $4.bitfield); } @@ -3638,6 +3643,7 @@ c_constructor_decl : storage_class type LPAREN parms RPAREN ctor_end { Setattr($$,"throws",$6.throws); Setattr($$,"throw",$6.throwf); Setattr($$,"noexcept",$6.nexcept); + Setattr($$,"final",$6.final); err = 0; } } @@ -4704,6 +4710,7 @@ cpp_constructor_decl : storage_class type LPAREN parms RPAREN ctor_end { Setattr($$,"throws",$6.throws); Setattr($$,"throw",$6.throwf); Setattr($$,"noexcept",$6.nexcept); + Setattr($$,"final",$6.final); if (Len(scanner_ccode)) { String *code = Copy(scanner_ccode); Setattr($$,"code",code); @@ -4740,6 +4747,7 @@ cpp_destructor_decl : NOT idtemplate LPAREN parms RPAREN cpp_end { Setattr($$,"throws",$6.throws); Setattr($$,"throw",$6.throwf); Setattr($$,"noexcept",$6.nexcept); + Setattr($$,"final",$6.final); if ($6.val) Setattr($$,"value",$6.val); if ($6.qualifier) @@ -4760,6 +4768,7 @@ cpp_destructor_decl : NOT idtemplate LPAREN parms RPAREN cpp_end { Setattr($$,"throws",$7.throws); Setattr($$,"throw",$7.throwf); Setattr($$,"noexcept",$7.nexcept); + Setattr($$,"final",$7.final); if ($7.val) Setattr($$,"value",$7.val); if (Len(scanner_ccode)) { @@ -4941,6 +4950,7 @@ cpp_end : cpp_const SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; } | cpp_const EQUAL default_delete SEMI { Clear(scanner_ccode); @@ -4951,6 +4961,7 @@ cpp_end : cpp_const SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; } | cpp_const LBRACE { skip_balanced('{','}'); @@ -4961,6 +4972,7 @@ cpp_end : cpp_const SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; } ; @@ -4973,6 +4985,7 @@ cpp_vend : cpp_const SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; } | cpp_const EQUAL definetype SEMI { Clear(scanner_ccode); @@ -4982,7 +4995,8 @@ cpp_vend : cpp_const SEMI { $$.bitfield = 0; $$.throws = $1.throws; $$.throwf = $1.throwf; - $$.nexcept = $1.nexcept; + $$.nexcept = $1.nexcept; + $$.final = $1.final; } | cpp_const LBRACE { skip_balanced('{','}'); @@ -4992,7 +5006,8 @@ cpp_vend : cpp_const SEMI { $$.bitfield = 0; $$.throws = $1.throws; $$.throwf = $1.throwf; - $$.nexcept = $1.nexcept; + $$.nexcept = $1.nexcept; + $$.final = $1.final; } ; @@ -5210,6 +5225,7 @@ def_args : EQUAL definetype { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } } | EQUAL definetype LBRACKET expr RBRACKET { @@ -5223,6 +5239,7 @@ def_args : EQUAL definetype { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } else { $$.val = NewStringf("%s[%s]",$2.val,$4.val); } @@ -5236,6 +5253,7 @@ def_args : EQUAL definetype { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | COLON expr { $$.val = 0; @@ -5245,6 +5263,7 @@ def_args : EQUAL definetype { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | empty { $$.val = 0; @@ -5254,6 +5273,7 @@ def_args : EQUAL definetype { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } ; @@ -6293,6 +6313,7 @@ definetype : { /* scanner_check_typedef(); */ } expr { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; scanner_ignore_typedef(); } | default_delete { @@ -6319,6 +6340,7 @@ deleted_definition : DELETE_KW { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } ; @@ -6333,6 +6355,7 @@ explicit_default : DEFAULT { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } ; @@ -6525,6 +6548,7 @@ valexpr : exprnum { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | WCHARCONST { $$.val = NewString($1); @@ -6538,6 +6562,7 @@ valexpr : exprnum { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } /* grouping */ @@ -6892,18 +6917,18 @@ virt_specifier_seq : OVERRIDE { $$ = 0; } | FINAL { - $$ = 0; + $$ = NewString("1"); } | FINAL OVERRIDE { - $$ = 0; + $$ = NewString("1"); } | OVERRIDE FINAL { - $$ = 0; + $$ = NewString("1"); } ; virt_specifier_seq_opt : virt_specifier_seq { - $$ = 0; + $$ = $1; } | empty { $$ = 0; @@ -6914,31 +6939,37 @@ exception_specification : THROW LPAREN parms RPAREN { $$.throws = $3; $$.throwf = NewString("1"); $$.nexcept = 0; + $$.final = 0; } | NOEXCEPT { $$.throws = 0; $$.throwf = 0; $$.nexcept = NewString("true"); + $$.final = 0; } | virt_specifier_seq { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = $1; } | THROW LPAREN parms RPAREN virt_specifier_seq { $$.throws = $3; $$.throwf = NewString("1"); $$.nexcept = 0; + $$.final = $5; } | NOEXCEPT virt_specifier_seq { $$.throws = 0; $$.throwf = 0; $$.nexcept = NewString("true"); + $$.final = $2; } | NOEXCEPT LPAREN expr RPAREN { $$.throws = 0; $$.throwf = 0; $$.nexcept = $3.val; + $$.final = 0; } ; @@ -6946,6 +6977,7 @@ qualifiers_exception_specification : cv_ref_qualifier { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; $$.qualifier = $1.qualifier; $$.refqualifier = $1.refqualifier; } @@ -6968,6 +7000,7 @@ cpp_const : qualifiers_exception_specification { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; $$.qualifier = 0; $$.refqualifier = 0; } @@ -6980,6 +7013,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; if ($1.qualifier) Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n"); } @@ -6990,6 +7024,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; if ($1.qualifier) Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n"); } @@ -7001,6 +7036,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | LPAREN parms RPAREN LBRACE { skip_balanced('{','}'); @@ -7010,6 +7046,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | EQUAL definetype SEMI { $$.have_parms = 0; @@ -7017,6 +7054,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = 0; $$.throwf = 0; $$.nexcept = 0; + $$.final = 0; } | exception_specification EQUAL default_delete SEMI { $$.have_parms = 0; @@ -7024,6 +7062,7 @@ ctor_end : cpp_const ctor_initializer SEMI { $$.throws = $1.throws; $$.throwf = $1.throwf; $$.nexcept = $1.nexcept; + $$.final = $1.final; if ($1.qualifier) Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n"); } diff --git a/Source/Include/swigwarn.h b/Source/Include/swigwarn.h index fde82bd96..fbcea4dbd 100644 --- a/Source/Include/swigwarn.h +++ b/Source/Include/swigwarn.h @@ -211,6 +211,7 @@ #define WARN_LANG_EXTEND_CONSTRUCTOR 522 #define WARN_LANG_EXTEND_DESTRUCTOR 523 #define WARN_LANG_EXPERIMENTAL 524 +#define WARN_LANG_DIRECTOR_FINAL 525 /* -- Doxygen comments -- */ diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index f9af9723a..f7979b611 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -1894,6 +1894,8 @@ int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int default_ } if (!checkAttribute(nn, "storage", "virtual")) continue; + if (GetFlag(nn, "final")) + continue; /* we need to add methods(cdecl) and destructor (to check for throw decl) */ int is_destructor = (Cmp(nodeType, "destructor") == 0); if ((Cmp(nodeType, "cdecl") == 0) || is_destructor) { @@ -2109,7 +2111,7 @@ int Language::classDirectorMethods(Node *n) { Node *item = Getitem(vtable, i); String *method = Getattr(item, "methodNode"); String *fqdname = Getattr(item, "fqdname"); - if (GetFlag(method, "feature:nodirector")) + if (GetFlag(method, "feature:nodirector") || GetFlag(method, "final")) continue; String *wrn = Getattr(method, "feature:warnfilter"); @@ -2198,6 +2200,16 @@ int Language::classDirector(Node *n) { String *using_protected_members_code = NewString(""); for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) { Node *nodeType = Getattr(ni, "nodeType"); + if (Cmp(nodeType, "destructor") == 0 && GetFlag(ni, "final")) { + String *classtype = Getattr(n, "classtype"); + SWIG_WARN_NODE_BEGIN(ni); + Swig_warning(WARN_LANG_DIRECTOR_FINAL, input_file, line_number, "Destructor %s is final, %s cannot be a director class.\n", Swig_name_decl(ni), classtype); + SWIG_WARN_NODE_END(ni); + SetFlag(n, "feature:nodirector"); + Delete(vtable); + Delete(using_protected_members_code); + return SWIG_OK; + } bool cdeclaration = (Cmp(nodeType, "cdecl") == 0); if (cdeclaration && !GetFlag(ni, "feature:ignore")) { if (isNonVirtualProtectedAccess(ni)) {