diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html index 0d2f22aff6f..9d315d8e35c 100644 --- a/Doc/Manual/CPlusPlus11.html +++ b/Doc/Manual/CPlusPlus11.html @@ -414,6 +414,29 @@

7.2.10 Object cons }; +

Explicit overrides and final

+ +

+The special identifiers final and override can be used on methods and destructors, +such as in the following example: +

+ +
+struct BaseStruct {
+  virtual void ab() const = 0;
+  virtual void cd();
+  virtual void ef();
+  virtual ~BaseStruct();
+};
+struct DerivedStruct : BaseStruct {
+  virtual void ab() const override;
+  virtual void cd() final;
+  virtual void ef() final override;
+  virtual ~DerivedStruct() override;
+};
+
+ +

7.2.11 Null pointer constant

diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 642e7fd8cc2..fb53a0ec9b6 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -490,6 +490,7 @@ CPP11_TEST_CASES = \ cpp11_default_delete \ cpp11_delegating_constructors \ cpp11_explicit_conversion_operators \ + cpp11_final_override \ cpp11_function_objects \ cpp11_inheriting_constructors \ cpp11_initializer_list \ diff --git a/Examples/test-suite/cpp11_final_override.i b/Examples/test-suite/cpp11_final_override.i new file mode 100644 index 00000000000..f691f8770a1 --- /dev/null +++ b/Examples/test-suite/cpp11_final_override.i @@ -0,0 +1,128 @@ +// Test C++11 virtual specifier sequences (final and/or override on methods) +// Also check final/override - the two 'identifiers with special meaning' work as normal identifiers + +%module cpp11_final_override + +%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' + +%inline %{ + +struct Base { + virtual void stuff() const {} + virtual void override1() const {} + virtual void override2() const {} + virtual void finaloverride1() {} + virtual void finaloverride2() {} + virtual void finaloverride3() {} + virtual void finaloverride4() const {} + virtual ~Base() {} +}; + +struct Derived /*final*/ : Base { + virtual void stuff() const noexcept override final {} + virtual void override1() const noexcept override; + virtual void override2() const noexcept override; + virtual void final1() final {} + virtual void final2() noexcept final {} + virtual void final4() const final {} + virtual void final5() const noexcept final {} + virtual void finaloverride1() final override {} + virtual void finaloverride2() override final {} + virtual void finaloverride3() noexcept override final {} + virtual void finaloverride4() const noexcept override final {} + virtual ~Derived() override final {} +}; +void Derived::override2() const noexcept {} + +// Pure virtual methods +struct PureBase { + virtual void pure1(int) const = 0; + virtual void pure2(int) const = 0; + virtual void pure3(int) const = 0; + virtual void pure4(int) const = 0; + virtual void pure5(int) const = 0; + virtual ~PureBase() {} +}; + +struct PureDerived : PureBase { + virtual void pure1(int) const override = 0; + virtual void pure2(int) const final = 0; + virtual void pure3(int) const override final = 0; + virtual void pure4(int) const final override = 0; + virtual void pure5(int) const noexcept final override = 0; + virtual ~PureDerived() override final; +}; +void PureDerived::pure1(int) const {} +void PureDerived::pure2(int) const {} +void PureDerived::pure3(int) const {} +void PureDerived::pure4(int) const {} +void PureDerived::pure5(int) const noexcept {} +PureDerived::~PureDerived() {} + +// Destructors and virtual specifier sequences (final/override) +struct Destructors1 : Base { + virtual ~Destructors1() override {} +}; +struct Destructors2 : Base { + virtual ~Destructors2() final {} +}; +struct Destructors3 : Base { + virtual ~Destructors3() noexcept final override {} +}; +struct Destructors4 : Base { + virtual ~Destructors4() noexcept override final {} +}; + +// Check the two 'identifiers with special meaning' work as normal identifiers +struct FinalOverrideMethods { + virtual void final() {} + virtual void override(int) {} +}; +struct FinalOverrideVariables { + int final; + double override; +}; +void final(int) {} +void override() {} +%} + +%{ +void Derived::override1() const noexcept {} +%} + +// Example in documentation ... declarations only +%inline %{ +struct BaseStruct { + virtual void ab() const = 0; + virtual void cd(); + virtual void ef(); + virtual ~BaseStruct(); +}; +struct DerivedStruct : BaseStruct { + virtual void ab() const override; + virtual void cd() final; + virtual void ef() final override; + virtual ~DerivedStruct() override; +}; +struct DerivedNoVirtualStruct : BaseStruct { + void ab() const override; + void cd() final; + void ef() final override; + ~DerivedNoVirtualStruct() override; +}; +%} + +%{ +void BaseStruct::cd() {} +void BaseStruct::ef() {} +BaseStruct::~BaseStruct() {} +void DerivedStruct::ab() const {} +void DerivedStruct::cd() {} +void DerivedStruct::ef() {} +DerivedStruct::~DerivedStruct() {} +void DerivedNoVirtualStruct::ab() const {} +void DerivedNoVirtualStruct::cd() {} +void DerivedNoVirtualStruct::ef() {} +DerivedNoVirtualStruct::~DerivedNoVirtualStruct() {} +%} diff --git a/Source/CParse/cscanner.c b/Source/CParse/cscanner.c index 2dfc2c47951..4e02b4b6222 100644 --- a/Source/CParse/cscanner.c +++ b/Source/CParse/cscanner.c @@ -748,18 +748,18 @@ int yylex(void) { yylval.intvalue = cparse_line; return (TEMPLATE); } - if (strcmp(yytext, "delete") == 0) { + if (strcmp(yytext, "delete") == 0) return (DELETE_KW); - } - if (strcmp(yytext, "default") == 0) { + if (strcmp(yytext, "default") == 0) return (DEFAULT); - } - if (strcmp(yytext, "using") == 0) { + if (strcmp(yytext, "using") == 0) return (USING); - } - if (strcmp(yytext, "namespace") == 0) { + if (strcmp(yytext, "namespace") == 0) return (NAMESPACE); - } + if (strcmp(yytext, "override") == 0) + return (OVERRIDE); + if (strcmp(yytext, "final") == 0) + return (FINAL); } else { if (strcmp(yytext, "class") == 0) { Swig_warning(WARN_PARSE_CLASS_KEYWORD, cparse_file, cparse_line, "class keyword used, but not in C++ mode.\n"); diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index a7604ff56de..fe8b7f97f53 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1388,8 +1388,9 @@ static void mark_nodes_as_extend(Node *n) { %token ILLEGAL CONSTANT %token NAME RENAME NAMEWARN EXTEND PRAGMA FEATURE VARARGS %token ENUM -%token CLASS TYPENAME PRIVATE PUBLIC PROTECTED COLON STATIC VIRTUAL FRIEND THROW CATCH EXPLICIT AUTO NOEXCEPT -%token STATIC_ASSERT CONSTEXPR THREAD_LOCAL DECLTYPE /* C++11 keywords */ +%token CLASS TYPENAME PRIVATE PUBLIC PROTECTED COLON STATIC VIRTUAL FRIEND THROW CATCH EXPLICIT +%token STATIC_ASSERT CONSTEXPR THREAD_LOCAL DECLTYPE AUTO NOEXCEPT /* C++11 keywords */ +%token OVERRIDE FINAL /* C++11 identifiers with special meaning */ %token USING %token NAMESPACE %token NATIVE INLINE @@ -1443,6 +1444,7 @@ static void mark_nodes_as_extend(Node *n) { %type kwargs options; /* Misc */ +%type identifier; %type initializer cpp_const exception_specification; %type storage_class; %type parms ptail rawparms varargs_parms ; @@ -1480,6 +1482,7 @@ static void mark_nodes_as_extend(Node *n) { %type lambda_introducer lambda_body; %type lambda_tail; %type optional_constant_directive; +%type virt_specifier_seq; %% @@ -1701,7 +1704,7 @@ clear_directive : CLEAR tm_list SEMI { %constant type name = value; ------------------------------------------------------------ */ -constant_directive : CONSTANT ID EQUAL definetype SEMI { +constant_directive : CONSTANT identifier EQUAL definetype SEMI { if (($4.type != T_ERROR) && ($4.type != T_SYMBOL)) { SwigType *type = NewSwigType($4.type); $$ = new_node("constant"); @@ -1783,7 +1786,7 @@ echo_directive : ECHO HBLOCK { %except; ------------------------------------------------------------ */ -except_directive : EXCEPT LPAREN ID RPAREN LBRACE { +except_directive : EXCEPT LPAREN identifier RPAREN LBRACE { skip_balanced('{','}'); $$ = 0; Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n"); @@ -1795,7 +1798,7 @@ except_directive : EXCEPT LPAREN ID RPAREN LBRACE { Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n"); } - | EXCEPT LPAREN ID RPAREN SEMI { + | EXCEPT LPAREN identifier RPAREN SEMI { $$ = 0; Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n"); } @@ -2080,13 +2083,13 @@ name_directive : NAME LPAREN idstring RPAREN { %native(scriptname) type name (parms); ------------------------------------------------------------ */ -native_directive : NATIVE LPAREN ID RPAREN storage_class ID SEMI { +native_directive : NATIVE LPAREN identifier RPAREN storage_class identifier SEMI { $$ = new_node("native"); Setattr($$,"name",$3); Setattr($$,"wrap:name",$6); add_symbols($$); } - | NATIVE LPAREN ID RPAREN storage_class type declarator SEMI { + | NATIVE LPAREN identifier RPAREN storage_class type declarator SEMI { if (!SwigType_isfunction($7.type)) { Swig_error(cparse_file,cparse_line,"%%native declaration '%s' is not a function.\n", $7.id); $$ = 0; @@ -2112,13 +2115,13 @@ native_directive : NATIVE LPAREN ID RPAREN storage_class ID SEMI { %pragma name ------------------------------------------------------------ */ -pragma_directive : PRAGMA pragma_lang ID EQUAL pragma_arg { +pragma_directive : PRAGMA pragma_lang identifier EQUAL pragma_arg { $$ = new_node("pragma"); Setattr($$,"lang",$2); Setattr($$,"name",$3); Setattr($$,"value",$5); } - | PRAGMA pragma_lang ID { + | PRAGMA pragma_lang identifier { $$ = new_node("pragma"); Setattr($$,"lang",$2); Setattr($$,"name",$3); @@ -2129,13 +2132,12 @@ pragma_arg : string { $$ = NewString($1); } | HBLOCK { $$ = $1; } ; -pragma_lang : LPAREN ID RPAREN { $$ = $2; } +pragma_lang : LPAREN identifier RPAREN { $$ = $2; } | empty { $$ = (char *) "swig"; } ; /* ------------------------------------------------------------ - %rename identifier newname; - %rename identifier "newname"; + %rename(newname) identifier; ------------------------------------------------------------ */ rename_directive : rename_namewarn declarator idstring SEMI { @@ -2883,7 +2885,7 @@ c_declaration : c_decl { $$ = 0; /* TODO - ignored for now */ } - | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL ID { + | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL identifier { skip_decl(); $$ = new_node("using"); Setattr($$,"uname",$8); @@ -4221,7 +4223,7 @@ cpp_namespace_decl : NAMESPACE idcolon LBRACE { Namespaceprefix = Swig_symbol_qualifiedscopename(0); add_symbols($$); } - | NAMESPACE ID EQUAL idcolon SEMI { + | NAMESPACE identifier EQUAL idcolon SEMI { /* Namespace alias */ Node *n; $$ = new_node("namespace"); @@ -5810,7 +5812,7 @@ explicit_default : DEFAULT { /* Some stuff for handling enums */ -ename : ID { $$ = $1; } +ename : identifier { $$ = $1; } | empty { $$ = (char *) 0;} ; @@ -5837,7 +5839,7 @@ enumlist : enumlist COMMA optional_constant_directive edecl optional_cons } ; -edecl : ID { +edecl : identifier { SwigType *type = NewSwigType(T_INT); $$ = new_node("enumitem"); Setattr($$,"name",$1); @@ -5845,7 +5847,7 @@ edecl : ID { SetFlag($$,"feature:immutable"); Delete(type); } - | ID EQUAL etype { + | identifier EQUAL etype { SwigType *type = NewSwigType($3.type == T_BOOL ? T_BOOL : ($3.type == T_CHAR ? T_CHAR : T_INT)); $$ = new_node("enumitem"); Setattr($$,"name",$1); @@ -6263,6 +6265,20 @@ opt_virtual : VIRTUAL | empty ; +virt_specifier_seq : OVERRIDE { + $$ = 0; + } + | FINAL { + $$ = 0; + } + | FINAL OVERRIDE { + $$ = 0; + } + | OVERRIDE FINAL { + $$ = 0; + } + ; + exception_specification : THROW LPAREN parms RPAREN { $$.throws = $3; $$.throwf = NewString("1"); @@ -6273,7 +6289,16 @@ exception_specification : THROW LPAREN parms RPAREN { $$.throwf = 0; $$.nexcept = NewString("true"); } - + | virt_specifier_seq { + $$.throws = 0; + $$.throwf = 0; + $$.nexcept = 0; + } + | NOEXCEPT virt_specifier_seq { + $$.throws = 0; + $$.throwf = 0; + $$.nexcept = NewString("true"); + } | NOEXCEPT LPAREN expr RPAREN { $$.throws = 0; $$.throwf = 0; @@ -6390,7 +6415,13 @@ template_decl : LESSTHAN valparms GREATERTHAN { | empty { $$ = (char*)""; } ; -idstring : ID { $$ = $1; } +/* Identifiers including the C++11 identifiers with special meaning */ +identifier : ID { $$ = $1; } + | OVERRIDE { $$ = Swig_copy_string("override"); } + | FINAL { $$ = Swig_copy_string("final"); } + ; + +idstring : identifier { $$ = $1; } | default_delete { $$ = $1.val; } | string { $$ = $1; } ; @@ -6442,7 +6473,7 @@ idcolontail : DCOLON idtemplate idcolontail { ; -idtemplate : ID template_decl { +idtemplate : identifier template_decl { $$ = NewStringf("%s%s",$1,$2); /* if (Len($2)) { scanner_last_id(1); @@ -6451,19 +6482,19 @@ idtemplate : ID template_decl { ; /* Identifier, but no templates */ -idcolonnt : ID idcolontailnt { +idcolonnt : identifier idcolontailnt { $$ = 0; if (!$$) $$ = NewStringf("%s%s", $1,$2); Delete($2); } - | NONID DCOLON ID idcolontailnt { + | NONID DCOLON identifier idcolontailnt { $$ = NewStringf("::%s%s",$3,$4); Delete($4); } - | ID { + | identifier { $$ = NewString($1); } - | NONID DCOLON ID { + | NONID DCOLON identifier { $$ = NewStringf("::%s",$3); } | OPERATOR { @@ -6474,17 +6505,17 @@ idcolonnt : ID idcolontailnt { } ; -idcolontailnt : DCOLON ID idcolontailnt { +idcolontailnt : DCOLON identifier idcolontailnt { $$ = NewStringf("::%s%s",$2,$3); Delete($3); } - | DCOLON ID { + | DCOLON identifier { $$ = NewStringf("::%s",$2); } | DCOLON OPERATOR { $$ = NewStringf("::%s",$2); } - | DCNOT ID { + | DCNOT identifier { $$ = NewStringf("::~%s",$2); } ;