From 1cf599bccb100f3bcda42f5f507ea32f99cd5f89 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Wed, 23 Aug 2017 09:07:12 +0100 Subject: [PATCH] Improve ref-qualifier implementation Internally, handle function ref-qualifiers in the function decl type string. Needed for a whole host of things to work like %feature and %rename. Add %feature %rename and %ignore testing for ref-qualifiers. --- Examples/test-suite/cpp11_ref_qualifiers.i | 82 ++++++++++++++++--- Examples/test-suite/errors/cpp_refqualifier.i | 4 + .../test-suite/errors/cpp_refqualifier.stderr | 12 +-- .../java/cpp11_ref_qualifiers_runme.java | 25 ++++++ .../python/cpp11_ref_qualifiers_runme.py | 31 +++++++ Source/CParse/parser.y | 20 +++-- Source/Modules/overload.cxx | 18 +++- Source/Swig/naming.c | 23 ++++-- Source/Swig/stype.c | 20 +++-- Source/Swig/swig.h | 1 + Source/Swig/typeobj.c | 81 ++++++++++++++++-- 11 files changed, 265 insertions(+), 52 deletions(-) diff --git a/Examples/test-suite/cpp11_ref_qualifiers.i b/Examples/test-suite/cpp11_ref_qualifiers.i index fa1d68d0e..2115843a3 100644 --- a/Examples/test-suite/cpp11_ref_qualifiers.i +++ b/Examples/test-suite/cpp11_ref_qualifiers.i @@ -1,16 +1,76 @@ %module cpp11_ref_qualifiers -%inline %{ -class Host { -public: - void h1() & {} - void h2() const & {} - void h3() && {} - void h4() const && {} +%include - void h() & {} - void h() const & {} - void h() && {} - void h() const && {} +%ignore Host::h() const &; + +// Basic testing +%inline %{ +using std::string; +class Host { + string s; +public: + string h1() & { return string(); } + string h2() const & { return string(); } + string h3() && { return std::move(string()); } + string h4() const && { return std::move(string()); } + string h5() const { return string(); } + string h6() volatile const & { return string(); } + string h7() const volatile & { return string(); } + string h8() volatile const && { return std::move(string()); } + string h9() const volatile && { return std::move(string()); } + + string h() & { return string(); } + string h() const & { return string(); } + string h() && { return std::move(string()); } + string h() const && { return std::move(string()); } +}; +%} + +// %feature testing +%feature("except") F1() & %{ result = "F1"; %} +%feature("except") F2 %{ result = "F2"; %} +%feature("except") F3 %{ result = "F3"; %} +%feature("except") F3() %{ _should_not_be_used_ %} + +%feature("except") C1(int i) const & %{ result = "C1"; %} +%feature("except") C2 %{ result = "C2"; %} +%feature("except") C3 %{ result = "C3"; %} +%feature("except") C3(int i) %{ _should_not_be_used_ %} + +%inline %{ +struct Features { + string F1() & { return string(); } + string F2() & { return string(); } + string F3() & { return string(); } + + string C1(int i) const & { return string(); } + string C2(int i) const & { return string(); } + string C3(int i) const & { return string(); } +}; +%} + +// %rename testing +%rename(RR1) R1; +%rename(RR2) R2() &; +%rename(RR3) R3; +%rename(RR3Bad) R3(); + +%rename(SS1) S1; +%rename(SS2) S2(int i) const &; +%rename(SS3) S3; +%rename(SS3Bad) S3(int i); +%rename(SS3BadConst) S3(int i) const; +%rename(SS3BadLValue) S3(int i) &; + +%inline %{ +struct Renames { + string R1() & { return string(); } + string R2() & { return string(); } + string R3() & { return string(); } + + string S1(int i) const & { return string(); } + string S2(int i) const & { return string(); } + string S3(int i) const & { return string(); } }; %} diff --git a/Examples/test-suite/errors/cpp_refqualifier.i b/Examples/test-suite/errors/cpp_refqualifier.i index 1cbcd936d..438aacd93 100644 --- a/Examples/test-suite/errors/cpp_refqualifier.i +++ b/Examples/test-suite/errors/cpp_refqualifier.i @@ -1,6 +1,8 @@ %module cpp_refqualifier %ignore Host::h_ignored; +%ignore Host::i_ignored() &&; +%ignore Host::j_ignored() const &&; class Host { public: @@ -15,4 +17,6 @@ public: void h() const &&; void h_ignored() &&; + void i_ignored() &&; + void j_ignored() const &&; }; diff --git a/Examples/test-suite/errors/cpp_refqualifier.stderr b/Examples/test-suite/errors/cpp_refqualifier.stderr index 866d9b530..ea2cd220a 100644 --- a/Examples/test-suite/errors/cpp_refqualifier.stderr +++ b/Examples/test-suite/errors/cpp_refqualifier.stderr @@ -1,6 +1,6 @@ -cpp_refqualifier.i:9: Warning 405: Method with rvalue ref-qualifier ignored h3() &&. -cpp_refqualifier.i:10: Warning 405: Method with rvalue ref-qualifier ignored h4() const &&. -cpp_refqualifier.i:14: Warning 405: Method with rvalue ref-qualifier ignored h() &&. -cpp_refqualifier.i:15: Warning 405: Method with rvalue ref-qualifier ignored h() const &&. -cpp_refqualifier.i:13: Warning 512: Overloaded method Host::h() const & ignored, -cpp_refqualifier.i:12: Warning 512: using non-const method Host::h() & instead. +cpp_refqualifier.i:11: Warning 405: Method with rvalue ref-qualifier h3() && ignored. +cpp_refqualifier.i:12: Warning 405: Method with rvalue ref-qualifier h4() const && ignored. +cpp_refqualifier.i:16: Warning 405: Method with rvalue ref-qualifier h() && ignored. +cpp_refqualifier.i:17: Warning 405: Method with rvalue ref-qualifier h() const && ignored. +cpp_refqualifier.i:15: Warning 512: Overloaded method Host::h() const & ignored, +cpp_refqualifier.i:14: Warning 512: using non-const method Host::h() & instead. diff --git a/Examples/test-suite/java/cpp11_ref_qualifiers_runme.java b/Examples/test-suite/java/cpp11_ref_qualifiers_runme.java index 49afe8039..bbe6be8e3 100644 --- a/Examples/test-suite/java/cpp11_ref_qualifiers_runme.java +++ b/Examples/test-suite/java/cpp11_ref_qualifiers_runme.java @@ -14,9 +14,34 @@ public class cpp11_ref_qualifiers_runme { public static void main(String argv[]) { Host h = new Host(); + + // Basic testing h.h1(); h.h2(); + h.h6(); + h.h7(); + h.h(); + + // %feature testing + Features f = new Features(); + if (!f.F1().equals("F1")) throw new RuntimeException("Fail"); + if (!f.F2().equals("F2")) throw new RuntimeException("Fail"); + if (!f.F3().equals("F3")) throw new RuntimeException("Fail"); + + if (!f.C1(0).equals("C1")) throw new RuntimeException("Fail"); + if (!f.C2(0).equals("C2")) throw new RuntimeException("Fail"); + if (!f.C3(0).equals("C3")) throw new RuntimeException("Fail"); + + // %rename testing + Renames r = new Renames(); + r.RR1(); + r.RR2(); + r.RR3(); + + r.SS1(0); + r.SS2(0); + r.SS3(0); } } diff --git a/Examples/test-suite/python/cpp11_ref_qualifiers_runme.py b/Examples/test-suite/python/cpp11_ref_qualifiers_runme.py index 24ce1d2bf..47c474218 100644 --- a/Examples/test-suite/python/cpp11_ref_qualifiers_runme.py +++ b/Examples/test-suite/python/cpp11_ref_qualifiers_runme.py @@ -1,6 +1,37 @@ import cpp11_ref_qualifiers h = cpp11_ref_qualifiers.Host() + +# Basic testing h.h1() h.h2() +h.h6() +h.h7() + h.h() + +# %feature testing +f = cpp11_ref_qualifiers.Features() +if f.F1() != "F1": + raise RuntimeException("Fail") +if f.F2() != "F2": + raise RuntimeException("Fail") +if f.F3() != "F3": + raise RuntimeException("Fail") + +if f.C1(0) != "C1": + raise RuntimeException("Fail") +if f.C2(0) != "C2": + raise RuntimeException("Fail") +if f.C3(0) != "C3": + raise RuntimeException("Fail") + +# %rename testing +r = cpp11_ref_qualifiers.Renames() +r.RR1() +r.RR2() +r.RR3() + +r.SS1(0) +r.SS2(0) +r.SS3(0) diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index bc12997be..ccb09e3eb 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -487,16 +487,16 @@ static void add_symbols(Node *n) { } { String *refqualifier = Getattr(n, "refqualifier"); - if (SwigType_isrvalue_reference(refqualifier) && strncmp(Char(symname), "$ignore", 7) != 0) { + if (SwigType_isrvalue_reference(refqualifier) && Strcmp(symname, "$ignore") != 0) { SWIG_WARN_NODE_BEGIN(n); Swig_warning(WARN_TYPE_RVALUE_REF_QUALIFIER_IGNORED, Getfile(n), Getline(n), - "Method with rvalue ref-qualifier ignored %s.\n", Swig_name_decl(n)); + "Method with rvalue ref-qualifier %s ignored.\n", Swig_name_decl(n)); SWIG_WARN_NODE_END(n); SetFlag(n, "feature:ignore"); } } } - if (only_csymbol || GetFlag(n,"feature:ignore") || strncmp(Char(symname),"$ignore",7) == 0) { + if (only_csymbol || GetFlag(n, "feature:ignore") || Strncmp(symname, "$ignore", 7) == 0) { /* Only add to C symbol table and continue */ Swig_symbol_add(0, n); if (!only_csymbol && !GetFlag(n, "feature:ignore")) { @@ -1421,10 +1421,12 @@ static void mark_nodes_as_extend(Node *n) { } /* ----------------------------------------------------------------------------- - * add_qualifier_to_declarator + * add_qualifier_to_declarator() * + * Normally the qualifier is pushed on to the front of the type. * Adding a qualifier to a pointer to member function is a special case. * For example : typedef double (Cls::*pmf)(void) const; + * The qualifier is : q(const). * The declarator is : m(Cls).f(void). * We need : m(Cls).q(const).f(void). * ----------------------------------------------------------------------------- */ @@ -5870,15 +5872,17 @@ pointer : STAR type_qualifier pointer { /* cv-qualifier plus C++11 ref-qualifier for non-static member functions */ cv_ref_qualifier : type_qualifier { $$.qualifier = $1; - $$.refqualifier = 0; + $$.refqualifier = 0; } | type_qualifier ref_qualifier { $$.qualifier = $1; - $$.refqualifier = $2; + $$.refqualifier = $2; + SwigType_push($$.qualifier, $2); } | ref_qualifier { - $$.qualifier = 0; - $$.refqualifier = $1; + $$.qualifier = NewStringEmpty(); + $$.refqualifier = $1; + SwigType_push($$.qualifier, $1); } ; diff --git a/Source/Modules/overload.cxx b/Source/Modules/overload.cxx index 330294efd..81d1bb000 100644 --- a/Source/Modules/overload.cxx +++ b/Source/Modules/overload.cxx @@ -231,9 +231,21 @@ List *Swig_overload_rank(Node *n, bool script_lang_wrapping) { } if (!differ) { /* See if declarations differ by const only */ - String *d1 = Getattr(nodes[i].n, "decl"); - String *d2 = Getattr(nodes[j].n, "decl"); - if (d1 && d2) { + String *decl1 = Getattr(nodes[i].n, "decl"); + String *decl2 = Getattr(nodes[j].n, "decl"); + if (decl1 && decl2) { + /* Remove ref-qualifiers. Note that rvalue ref-qualifiers are already ignored and + * it is illegal to overload a function with and without ref-qualifiers. So with + * all the combinations of ref-qualifiers and cv-qualifiers, we just detect + * the cv-qualifier (const) overloading. */ + String *d1 = Copy(decl1); + String *d2 = Copy(decl2); + if (SwigType_isreference(d1) || SwigType_isrvalue_reference(d1)) { + Delete(SwigType_pop(d1)); + } + if (SwigType_isreference(d2) || SwigType_isrvalue_reference(d2)) { + Delete(SwigType_pop(d2)); + } String *dq1 = Copy(d1); String *dq2 = Copy(d2); if (SwigType_isconst(d1)) { diff --git a/Source/Swig/naming.c b/Source/Swig/naming.c index 4fa32538f..d12770125 100644 --- a/Source/Swig/naming.c +++ b/Source/Swig/naming.c @@ -1689,18 +1689,23 @@ String *Swig_name_str(Node *n) { String *Swig_name_decl(Node *n) { String *qname; String *decl; - String *refqualifier = Getattr(n, "refqualifier"); qname = Swig_name_str(n); + decl = NewStringf("%s", qname); - if (checkAttribute(n, "kind", "variable")) - decl = NewStringf("%s", qname); - else - decl = NewStringf("%s(%s)%s", qname, ParmList_errorstr(Getattr(n, "parms")), SwigType_isconst(Getattr(n, "decl")) ? " const" : ""); - if (refqualifier) { - String *rq = SwigType_str(refqualifier, 0); - Printv(decl, " ", rq, NIL); - Delete(rq); + if (!checkAttribute(n, "kind", "variable")) { + String *d = Getattr(n, "decl"); + Printv(decl, "(", ParmList_errorstr(Getattr(n, "parms")), ")", NIL); + if (SwigType_isfunction(d)) { + SwigType *decl_temp = Copy(d); + SwigType *qualifiers = SwigType_pop_function_qualifiers(decl_temp); + if (qualifiers) { + String *qualifiers_string = SwigType_str(qualifiers, 0); + Printv(decl, " ", qualifiers_string, NIL); + Delete(qualifiers_string); + } + Delete(decl_temp); + } } Delete(qname); diff --git a/Source/Swig/stype.c b/Source/Swig/stype.c index 5cfa3e890..4b745b335 100644 --- a/Source/Swig/stype.c +++ b/Source/Swig/stype.c @@ -44,7 +44,7 @@ * 'z.' = Rvalue reference (&&) * 'a(n).' = Array of size n [n] * 'f(..,..).' = Function with arguments (args) - * 'q(str).' = Qualifier (such as const or volatile) (const, volatile) + * 'q(str).' = Qualifier, such as const or volatile (cv-qualifier) * 'm(cls).' = Pointer to member (cls::*) * * The encoding follows the order that you might describe a type in words. @@ -64,11 +64,19 @@ * * More examples: * - * String Encoding C++ Example - * --------------- ----------- - * p.f(bool).q(const).long const long (*)(bool) - * m(Funcs).q(const).f(bool).long long (Funcs::*)(bool) const - * r.q(const).m(Funcs).f(int).long long (Funcs::*const &)(int) + * String Encoding C++ Example + * --------------- ----------- + * p.f(bool).r.q(const).long const long & (*)(bool) + * m(Funcs).q(const).f(bool).long long (Funcs::*)(bool) const + * r.q(const).m(Funcs).f(int).long long (Funcs::*const &)(int) + * m(Funcs).z.q(const).f(bool).long long (Funcs::*)(bool) const && + * + * Function decl examples: + * + * f(bool). long a(bool); + * r.f(bool). long b(bool) &; + * z.f(bool). long c(bool) &&; + * z.q(const).f(bool). long d(bool) const &&; * * For the most part, this module tries to minimize the use of special * characters (*, [, <, etc...) in its type encoding. One reason for this diff --git a/Source/Swig/swig.h b/Source/Swig/swig.h index 0bcd53a66..7452c374c 100644 --- a/Source/Swig/swig.h +++ b/Source/Swig/swig.h @@ -136,6 +136,7 @@ extern "C" { extern SwigType *SwigType_add_function(SwigType *t, ParmList *parms); extern SwigType *SwigType_add_template(SwigType *t, ParmList *parms); extern SwigType *SwigType_pop_function(SwigType *t); + extern SwigType *SwigType_pop_function_qualifiers(SwigType *t); extern ParmList *SwigType_function_parms(const SwigType *t, Node *file_line_node); extern List *SwigType_split(const SwigType *t); extern String *SwigType_pop(SwigType *t); diff --git a/Source/Swig/typeobj.c b/Source/Swig/typeobj.c index 5240927ef..7a0626c29 100644 --- a/Source/Swig/typeobj.c +++ b/Source/Swig/typeobj.c @@ -43,11 +43,11 @@ * All type constructors are denoted by a trailing '.': * * 'p.' = Pointer (*) - * 'r.' = Reference (&) - * 'z.' = Rvalue reference (&&) + * 'r.' = Reference or ref-qualifier (&) + * 'z.' = Rvalue reference or ref-qualifier (&&) * 'a(n).' = Array of size n [n] * 'f(..,..).' = Function with arguments (args) - * 'q(str).' = Qualifier (such as const or volatile) (const, volatile) + * 'q(str).' = Qualifier, such as const or volatile (cv-qualifier) * 'm(cls).' = Pointer to member (cls::*) * * The complete type representation for varargs is: @@ -183,9 +183,10 @@ SwigType *SwigType_del_element(SwigType *t) { * SwigType_pop() * * Pop one type element off the type. - * Example: t in: q(const).p.Integer - * t out: p.Integer - * result: q(const). + * For example: + * t in: q(const).p.Integer + * t out: p.Integer + * result: q(const). * ----------------------------------------------------------------------------- */ SwigType *SwigType_pop(SwigType *t) { @@ -771,7 +772,6 @@ SwigType *SwigType_array_type(const SwigType *ty) { * Functions * * SwigType_add_function() - * SwigType_del_function() * SwigType_isfunction() * SwigType_pop_function() * @@ -795,14 +795,36 @@ SwigType *SwigType_add_function(SwigType *t, ParmList *parms) { return t; } +/* ----------------------------------------------------------------------------- + * SwigType_pop_function() + * + * Pop and return the function from the input type leaving the function's return + * type, if any. + * For example: + * t in: q(const).f().p. + * t out: p. + * result: q(const).f(). + * ----------------------------------------------------------------------------- */ + SwigType *SwigType_pop_function(SwigType *t) { SwigType *f = 0; SwigType *g = 0; char *c = Char(t); - if (strncmp(c, "q(", 2) == 0) { + if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) { + /* Remove ref-qualifier */ f = SwigType_pop(t); c = Char(t); } + if (strncmp(c, "q(", 2) == 0) { + /* Remove cv-qualifier */ + String *qual = SwigType_pop(t); + if (f) { + SwigType_push(qual, f); + Delete(f); + } + f = qual; + c = Char(t); + } if (strncmp(c, "f(", 2)) { printf("Fatal error. SwigType_pop_function applied to non-function.\n"); abort(); @@ -814,14 +836,55 @@ SwigType *SwigType_pop_function(SwigType *t) { return g; } +/* ----------------------------------------------------------------------------- + * SwigType_pop_function_qualifiers() + * + * Pop and return the function qualifiers from the input type leaving the rest of + * function declaration. Returns NULL if no qualifiers. + * For example: + * t in: r.q(const).f().p. + * t out: f().p. + * result: r.q(const) + * ----------------------------------------------------------------------------- */ + +SwigType *SwigType_pop_function_qualifiers(SwigType *t) { + SwigType *qualifiers = 0; + char *c = Char(t); + if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) { + /* Remove ref-qualifier */ + String *qual = SwigType_pop(t); + qualifiers = qual; + c = Char(t); + } + if (strncmp(c, "q(", 2) == 0) { + /* Remove cv-qualifier */ + String *qual = SwigType_pop(t); + if (qualifiers) { + SwigType_push(qual, qualifiers); + Delete(qualifiers); + } + qualifiers = qual; + c = Char(t); + } + assert(strncmp(c, "f(", 2) == 0); + + return qualifiers; +} + int SwigType_isfunction(const SwigType *t) { char *c; if (!t) { return 0; } c = Char(t); + if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) { + /* Might be a function with a ref-qualifier, skip over */ + c += 2; + if (!*c) + return 0; + } if (strncmp(c, "q(", 2) == 0) { - /* Might be a 'const' function. Try to skip over the 'const' */ + /* Might be a function with a cv-qualifier, skip over */ c = strchr(c, '.'); if (c) c++;