summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.current7
-rw-r--r--Doc/Manual/CPlusPlus11.html13
-rw-r--r--Examples/test-suite/common.mk2
-rw-r--r--Examples/test-suite/cpp11_final_class.i129
-rw-r--r--Examples/test-suite/cpp11_final_override.i2
-rw-r--r--Examples/test-suite/final_c.i11
-rw-r--r--Examples/test-suite/python/cpp11_final_class_runme.py62
-rw-r--r--Examples/test-suite/python/final_c_runme.py6
-rw-r--r--Source/CParse/parser.y59
9 files changed, 267 insertions, 24 deletions
diff --git a/CHANGES.current b/CHANGES.current
index 8a0f628b4..ef6fe9fde 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,13 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.1.0 (in progress)
===========================
+2022-10-05: olly, wsfulton
+ #672 Add support for parsing C++11 final classes such as:
+
+ class X final {};
+
+ This no longer gives a syntax error.
+
2022-10-05: wsfulton
[OCaml] Fix %rename for enum items. Previously the rename had no effect.
diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html
index 861b80048..a84ca5f51 100644
--- a/Doc/Manual/CPlusPlus11.html
+++ b/Doc/Manual/CPlusPlus11.html
@@ -891,6 +891,19 @@ struct DerivedStruct : BaseStruct {
};
</pre></div>
+<p>
+Classes can also be marked as final, such as
+</p>
+
+<div class="code"><pre>
+struct FinalDerivedStruct final : BaseStruct {
+ virtual void ab() const override;
+};
+</pre></div>
+
+<p>
+<b>Compatibility note:</b> Final methods were supported much earlier than final classes. SWIG-4.1.0 was the first version to support classes marked as final.
+</p>
<H3><a name="CPlusPlus11_null_pointer_constant">7.2.12 Null pointer constant</a></H3>
diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk
index a308d53c2..091293016 100644
--- a/Examples/test-suite/common.mk
+++ b/Examples/test-suite/common.mk
@@ -592,6 +592,7 @@ CPP11_TEST_CASES += \
cpp11_director_enums \
cpp11_directors \
cpp11_explicit_conversion_operators \
+ cpp11_final_class \
cpp11_final_directors \
cpp11_final_override \
cpp11_function_objects \
@@ -755,6 +756,7 @@ C_TEST_CASES += \
enum_macro \
enum_missing \
extern_declaration \
+ final_c \
funcptr \
function_typedef \
global_functions \
diff --git a/Examples/test-suite/cpp11_final_class.i b/Examples/test-suite/cpp11_final_class.i
new file mode 100644
index 000000000..e6a29e988
--- /dev/null
+++ b/Examples/test-suite/cpp11_final_class.i
@@ -0,0 +1,129 @@
+%module cpp11_final_class
+
+%warnfilter(SWIGWARN_PARSE_KEYWORD) final; // 'final' is a java keyword, renaming to '_final'
+%warnfilter(SWIGWARN_PARSE_KEYWORD) override; // 'override' is a C# keyword, renaming to '_override'
+
+%ignore Space1::final::operator=;
+#if defined(SWIGPHP)
+%rename(Space1_final) Space1::final::final;
+#endif
+#if defined(SWIGOCAML)
+%rename(finale) Space2::FinalEnum1::final;
+#endif
+
+%inline %{
+struct FinalBase {
+ virtual ~FinalBase() {}
+};
+
+struct FinalClass1 final : FinalBase {
+ void method1() {}
+};
+
+class FinalClass2 final : public FinalBase {
+public:
+ void method2() {}
+};
+
+struct FinalClass3 final {
+ void method3() {}
+};
+
+struct FinalClass4 {
+ void method4() {}
+} final;
+
+struct override final {
+ void omethod() {}
+};
+%}
+
+%rename(Space1_final) Space1::final;
+
+%inline %{
+namespace Space1 {
+struct final final {
+ void finalmethod() {}
+ final() {}
+ final(const final &other) = default;
+ final& operator=(const final &other) = default;
+};
+struct FinalClass5 final {
+ void method5() {}
+ final final_member_var;
+ final get_final_member() { return final_member_var; }
+ Space1::final get_final_member2() { return final_member_var; }
+};
+struct FinalClass6 {
+ void method6() {}
+ virtual void final() final {}
+ virtual ~FinalClass6() = default;
+};
+typedef final Space1_final_typedef1;
+typedef struct final Space1_final_typedef2;
+}
+typedef Space1::final Space1_final_typedef3;
+typedef struct Space1::final Space1_final_typedef4;
+%}
+
+%inline %{
+namespace Space2 {
+class Y {
+public:
+ Y(int i=0) {}
+};
+
+struct FinalVar1 {
+ class Y notfinal;
+// class Y final; // SWIG (C++ only) fails to parse (same for struct and union)
+};
+struct FinalVar2 {
+ class Y notfinal = {};
+// class Y final = {}; // SWIG (C++ only) fails to parse (same for struct and union)
+};
+struct FinalVar3 {
+ class Y notfinal = Y();
+// class Y final = Y(); // SWIG (C++ only) fails to parse (same for struct and union)
+};
+struct FinalVar4 {
+ class Y* final;
+ FinalVar4() : final() {}
+};
+struct FinalVar5 {
+ Y final;
+};
+struct FinalVar6 {
+ Y final = {};
+};
+struct FinalVar7 {
+ Y final = Y();
+};
+struct FinalVar8 {
+ Y final{};
+};
+struct FinalVar9 {
+ Y final{9};
+};
+struct FinalVar10 {
+ void a10(class Y final) {}
+ void b10(Y final) {}
+};
+struct FinalEnum1 {
+ enum Enum1 { one, two, final };
+ void enum_in(Enum1 e) {}
+};
+struct FinalEnum2 {
+ enum Enum2 { one, two, three, four };
+ enum Enum2 final;
+};
+}
+%}
+
+%rename(Space3_final) Space3::final;
+%inline %{
+namespace Space3 {
+ typedef struct final {
+ void fmethod() {}
+ } final;
+}
+%}
diff --git a/Examples/test-suite/cpp11_final_override.i b/Examples/test-suite/cpp11_final_override.i
index 849655b16..ac14326e5 100644
--- a/Examples/test-suite/cpp11_final_override.i
+++ b/Examples/test-suite/cpp11_final_override.i
@@ -27,7 +27,7 @@ struct Base {
virtual ~Base() {}
};
-struct Derived /*final*/ : Base {
+struct Derived final : Base {
virtual void stuff() const noexcept override final {}
virtual void override1() const noexcept override;
virtual void override2() const noexcept override;
diff --git a/Examples/test-suite/final_c.i b/Examples/test-suite/final_c.i
new file mode 100644
index 000000000..448d08d1e
--- /dev/null
+++ b/Examples/test-suite/final_c.i
@@ -0,0 +1,11 @@
+%module final_c
+
+%inline %{
+struct Y {
+ int yval;
+};
+struct Y final;
+void init() {
+ final.yval = 123;
+}
+%}
diff --git a/Examples/test-suite/python/cpp11_final_class_runme.py b/Examples/test-suite/python/cpp11_final_class_runme.py
new file mode 100644
index 000000000..568a1ca1a
--- /dev/null
+++ b/Examples/test-suite/python/cpp11_final_class_runme.py
@@ -0,0 +1,62 @@
+from cpp11_final_class import *
+
+fc1 = FinalClass1()
+fc1.method1()
+
+fc2 = FinalClass2()
+fc2.method2()
+
+fc3 = FinalClass3()
+fc3.method3()
+
+fc4 = FinalClass4()
+fc4.method4()
+fc4final = cvar.final
+cvar.final.method4()
+
+fc5 = FinalClass5()
+fc5.method5()
+fc5.final_member_var.finalmethod()
+fc5final = fc5.get_final_member()
+fc5final.finalmethod()
+fc5final = fc5.get_final_member2()
+fc5final.finalmethod()
+
+fc6 = FinalClass6()
+fc6.method6()
+fc6.final()
+
+o = override()
+o.omethod();
+
+y = Y()
+fv4 = FinalVar4()
+yy = fv4.final
+
+fv5 = FinalVar5()
+yy = fv5.final
+
+fv6 = FinalVar6()
+yy = fv6.final
+
+fv7 = FinalVar7()
+yy = fv7.final
+
+fv8 = FinalVar8()
+yy = fv8.final
+
+fv9 = FinalVar9()
+yy = fv9.final
+
+fv10 = FinalVar10()
+fv10.a10(y)
+fv10.b10(y)
+
+fe1 = FinalEnum1()
+fe1.enum_in(FinalEnum1.final)
+
+fe2 = FinalEnum2()
+fe2f = fe2.final
+
+s3f = Space3_final()
+s3f.fmethod();
diff --git a/Examples/test-suite/python/final_c_runme.py b/Examples/test-suite/python/final_c_runme.py
new file mode 100644
index 000000000..9ef4ded62
--- /dev/null
+++ b/Examples/test-suite/python/final_c_runme.py
@@ -0,0 +1,6 @@
+import final_c
+
+final_c.init()
+f = final_c.cvar.final
+if (f.yval != 123):
+ raise RuntimeError("f.yval fail")
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index d9c00809b..16ccb1249 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -13,14 +13,14 @@
* some point. Beware.
* ----------------------------------------------------------------------------- */
-/* There are 6 known shift-reduce conflicts in this file, fail compilation if any
- more are introduced.
+/* There are a small number of known shift-reduce conflicts in this file, fail
+ compilation if any more are introduced.
Please don't increase the number of the conflicts if at all possible. And if
you really have no choice but to do it, make sure you clearly document each
new conflict in this file.
*/
-%expect 6
+%expect 7
%{
#define yylex yylex
@@ -1716,6 +1716,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier)
%type <node> lambda_introducer lambda_body lambda_template;
%type <pl> lambda_tail;
%type <str> virt_specifier_seq virt_specifier_seq_opt;
+%type <str> class_virt_specifier_opt;
%%
@@ -3708,7 +3709,11 @@ cpp_declaration : cpp_class_decl { $$ = $1; }
/* A simple class/struct/union definition */
-cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
+
+/* Note that class_virt_specifier_opt for supporting final classes introduces one shift-reduce conflict
+ with C style variable declarations, such as: struct X final; */
+
+cpp_class_decl: storage_class cpptype idcolon class_virt_specifier_opt inherit LBRACE {
String *prefix;
List *bases = 0;
Node *scope = 0;
@@ -3716,10 +3721,10 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
$<node>$ = new_node("class");
Setline($<node>$,cparse_start_line);
Setattr($<node>$,"kind",$2);
- if ($4) {
- Setattr($<node>$,"baselist", Getattr($4,"public"));
- Setattr($<node>$,"protectedbaselist", Getattr($4,"protected"));
- Setattr($<node>$,"privatebaselist", Getattr($4,"private"));
+ if ($5) {
+ Setattr($<node>$,"baselist", Getattr($5,"public"));
+ Setattr($<node>$,"protectedbaselist", Getattr($5,"protected"));
+ Setattr($<node>$,"privatebaselist", Getattr($5,"private"));
}
Setattr($<node>$,"allows_typedef","1");
@@ -3747,8 +3752,8 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
Setattr($<node>$, "Classprefix", $3);
Classprefix = NewString($3);
/* Deal with inheritance */
- if ($4)
- bases = Swig_make_inherit_list($3,Getattr($4,"public"),Namespaceprefix);
+ if ($5)
+ bases = Swig_make_inherit_list($3,Getattr($5,"public"),Namespaceprefix);
prefix = SwigType_istemplate_templateprefix($3);
if (prefix) {
String *fbase, *tbase;
@@ -3828,7 +3833,7 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
Delattr($$, "prev_symtab");
/* Check for pure-abstract class */
- Setattr($$,"abstracts", pure_abstracts($7));
+ Setattr($$,"abstracts", pure_abstracts($8));
/* This bit of code merges in a previously defined %extend directive (if any) */
{
@@ -3844,12 +3849,12 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
scpname = Swig_symbol_qualifiedscopename(0);
Setattr(classes, scpname, $$);
- appendChild($$, $7);
+ appendChild($$, $8);
if (am)
Swig_extend_append_previous($$, am);
- p = $9;
+ p = $10;
if (p && !nscope_inner) {
if (!cparse_cplusplus && currentOuterClass)
appendChild(currentOuterClass, p);
@@ -3873,8 +3878,8 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
}
p = nextSibling(p);
}
- if ($9 && Cmp($1,"typedef") == 0)
- add_typedef_name($$, $9, $3, cscope, scpname);
+ if ($10 && Cmp($1,"typedef") == 0)
+ add_typedef_name($$, $10, $3, cscope, scpname);
Delete(scpname);
if (cplus_mode != CPLUS_PUBLIC) {
@@ -3896,12 +3901,12 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
if (cplus_mode == CPLUS_PRIVATE) {
$$ = 0; /* skip private nested classes */
} else if (cparse_cplusplus && currentOuterClass && ignore_nested_classes && !GetFlag($$, "feature:flatnested")) {
- $$ = nested_forward_declaration($1, $2, $3, Copy($3), $9);
+ $$ = nested_forward_declaration($1, $2, $3, Copy($3), $10);
} else if (nscope_inner) {
/* this is tricky */
/* we add the declaration in the original namespace */
if (Strcmp(nodeType(nscope_inner), "class") == 0 && cparse_cplusplus && ignore_nested_classes && !GetFlag($$, "feature:flatnested"))
- $$ = nested_forward_declaration($1, $2, $3, Copy($3), $9);
+ $$ = nested_forward_declaration($1, $2, $3, Copy($3), $10);
appendChild(nscope_inner, $$);
Swig_symbol_setscope(Getattr(nscope_inner, "symtab"));
Delete(Namespaceprefix);
@@ -3913,14 +3918,14 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
Swig_symbol_setscope(cscope);
Delete(Namespaceprefix);
Namespaceprefix = Swig_symbol_qualifiedscopename(0);
- add_symbols($9);
+ add_symbols($10);
if (nscope) {
$$ = nscope; /* here we return recreated namespace tower instead of the class itself */
- if ($9) {
- appendSibling($$, $9);
+ if ($10) {
+ appendSibling($$, $10);
}
} else if (!SwigType_istemplate(ty) && template_parameters == 0) { /* for template we need the class itself */
- $$ = $9;
+ $$ = $10;
}
} else {
Delete(yyrename);
@@ -3931,7 +3936,7 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
outer = Getattr(outer, "nested:outer");
appendSibling(outer, $$);
Swig_symbol_setscope(cscope); /* declaration goes in the parent scope */
- add_symbols($9);
+ add_symbols($10);
set_scope_to_global();
Delete(Namespaceprefix);
Namespaceprefix = Swig_symbol_qualifiedscopename(0);
@@ -3944,7 +3949,7 @@ cpp_class_decl : storage_class cpptype idcolon inherit LBRACE {
} else {
yyrename = Copy(Getattr($<node>$, "class_rename"));
add_symbols($$);
- add_symbols($9);
+ add_symbols($10);
Delattr($$, "class_rename");
}
}
@@ -7062,6 +7067,14 @@ virt_specifier_seq_opt : virt_specifier_seq {
}
;
+class_virt_specifier_opt : FINAL {
+ $$ = NewString("1");
+ }
+ | empty {
+ $$ = 0;
+ }
+ ;
+
exception_specification : THROW LPAREN parms RPAREN {
$$.throws = $3;
$$.throwf = NewString("1");