Add support for parsing C++11 final classes

Such as:

  class X final {};

This no longer gives a syntax error.
This change has introduced one more shift-reduce conflict in the parser.
with a conflict with a C style variable declaration with name final:

  class X final;

resulting in a syntax error (for C++ not C). This is an an unusual style
for C++ code and more typical declarations do work:

  X final;

Closes #672
This commit is contained in:
William S Fulton 2022-10-02 13:47:15 +01:00
parent 76f5670fa4
commit ba279ae939
9 changed files with 267 additions and 24 deletions

View File

@ -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.

View File

@ -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>

View File

@ -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 \

View File

@ -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;
}
%}

View File

@ -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;

View File

@ -0,0 +1,11 @@
%module final_c
%inline %{
struct Y {
int yval;
};
struct Y final;
void init() {
final.yval = 123;
}
%}

View File

@ -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();

View File

@ -0,0 +1,6 @@
import final_c
final_c.init()
f = final_c.cvar.final
if (f.yval != 123):
raise RuntimeError("f.yval fail")

View File

@ -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");