mirror of https://github.com/swig/swig
Implicit assignment operator detection fixes.
A class that does not have an explicit assignment operator does not have an implicit assignment operator if a member variable is not assignable. Similarly should one of the base classes also not be assignable. Detection of these scenarios has been fixed so that when wrapping a variable that is not assignable, a variable setter is not generated in order to avoid a compiler error. Template instantiation via %template is required in order for this to work for templates that are not assignable. Closes #1416
This commit is contained in:
parent
dad1cca849
commit
e07957ad4c
|
@ -7,6 +7,19 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
|
|||
Version 4.2.0 (in progress)
|
||||
===========================
|
||||
|
||||
2023-09-06: wsfulton
|
||||
#1416 Implicit assignment operator detection fixes.
|
||||
|
||||
A class that does not have an explicit assignment operator does not
|
||||
have an implicit assignment operator if a member variable is not
|
||||
assignable. Similarly should one of the base classes also not be
|
||||
assignable. Detection of these scenarios has been fixed so that when
|
||||
wrapping a variable that is not assignable, a variable setter is not
|
||||
generated in order to avoid a compiler error.
|
||||
|
||||
Template instantiation via %template is required in order for this to
|
||||
work for templates that are not assignable.
|
||||
|
||||
2023-09-03: wsfulton
|
||||
https://sourceforge.net/p/swig/bugs/1006/
|
||||
Fix incorrect variable setters being generated when the type of the
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
%module cpp11_assign_delete
|
||||
|
||||
%rename(Assign) *::operator=;
|
||||
|
||||
// (1) Test directly non-assignable member variables
|
||||
%inline %{
|
||||
struct AssignPublic {
|
||||
AssignPublic& operator=(const AssignPublic &) = delete;
|
||||
|
@ -16,6 +18,14 @@ private:
|
|||
AssignPrivate& operator=(const AssignPrivate &) = delete;
|
||||
};
|
||||
|
||||
struct MemberVars {
|
||||
// These will only have getters
|
||||
AssignPublic MemberPublic;
|
||||
AssignProtected MemberProtected;
|
||||
AssignPrivate MemberPrivate;
|
||||
};
|
||||
|
||||
// (2) Test indirectly non-assignable member variables via inheritance
|
||||
struct AssignPublicDerived : AssignPublic {};
|
||||
struct AssignProtectedDerived : AssignProtected {};
|
||||
struct AssignPrivateDerived : AssignPrivate {};
|
||||
|
@ -29,11 +39,8 @@ struct AssignPrivateDerivedSettable : AssignPrivate {
|
|||
AssignPrivateDerivedSettable& operator=(const AssignPrivateDerivedSettable &) { return *this; }
|
||||
};
|
||||
|
||||
struct MemberVars {
|
||||
struct InheritedMemberVars {
|
||||
// These will only have getters
|
||||
AssignPublic MemberPublic;
|
||||
AssignProtected MemberProtected;
|
||||
AssignPrivate MemberPrivate;
|
||||
AssignPublicDerived MemberPublicDerived;
|
||||
AssignProtectedDerived MemberProtectedDerived;
|
||||
AssignPrivateDerived MemberPrivateDerived;
|
||||
|
@ -44,3 +51,27 @@ struct MemberVars {
|
|||
AssignPrivateDerivedSettable MemberPrivateDerivedSettable;
|
||||
};
|
||||
%}
|
||||
|
||||
// (3) Test indirectly non-assignable member variables via classes that themselves have non-assignable member variables
|
||||
%inline %{
|
||||
struct MemberPublicVar {
|
||||
AssignPublic MemberPublic;
|
||||
};
|
||||
|
||||
struct MemberProtectedVar {
|
||||
protected:
|
||||
AssignProtected MemberProtected;
|
||||
};
|
||||
|
||||
struct MemberPrivateVar {
|
||||
private:
|
||||
AssignPrivate MemberPrivate;
|
||||
};
|
||||
|
||||
struct MembersMemberVars {
|
||||
// These will only have getters
|
||||
MemberPublicVar MemberPublic;
|
||||
MemberProtectedVar MemberProtected;
|
||||
MemberPrivateVar MemberPrivate;
|
||||
};
|
||||
%}
|
||||
|
|
|
@ -79,10 +79,9 @@ class Bar {
|
|||
Foo *testFoo(int a, Foo *f) {
|
||||
return new Foo(2 * a + (f ? f->num : 0) + fval.num);
|
||||
}
|
||||
/* Const member data and references mean this class can't be assigned.
|
||||
/* Const member data and references mean this class can't be assigned. */
|
||||
private:
|
||||
Bar& operator=(const Bar&);
|
||||
*/
|
||||
};
|
||||
|
||||
// This class is valid C++ but cannot be assigned to because it has const member data.
|
||||
|
|
|
@ -15,20 +15,34 @@ public class cpp11_assign_delete_runme {
|
|||
public static void main(String argv[]) {
|
||||
MemberVars mv = new MemberVars();
|
||||
|
||||
// (1) Test directly non-assignable member variables
|
||||
// These will only have getters
|
||||
AssignPublic a1 = mv.getMemberPublic();
|
||||
AssignProtected a2 = mv.getMemberProtected();
|
||||
AssignPrivate a3 = mv.getMemberPrivate();
|
||||
AssignPublicDerived a4 = mv.getMemberPublicDerived();
|
||||
AssignProtectedDerived a5 = mv.getMemberProtectedDerived();
|
||||
AssignPrivateDerived a6 = mv.getMemberPrivateDerived();
|
||||
|
||||
// (2) Test indirectly non-assignable member variables via inheritance
|
||||
InheritedMemberVars imv = new InheritedMemberVars();
|
||||
// These will only have getters
|
||||
AssignPublicDerived a4 = imv.getMemberPublicDerived();
|
||||
AssignProtectedDerived a5 = imv.getMemberProtectedDerived();
|
||||
AssignPrivateDerived a6 = imv.getMemberPrivateDerived();
|
||||
|
||||
// These will have getters and setters
|
||||
AssignPublicDerivedSettable a7 = mv.getMemberPublicDerivedSettable();
|
||||
mv.setMemberPublicDerivedSettable(a7);
|
||||
AssignProtectedDerivedSettable a8 = mv.getMemberProtectedDerivedSettable();
|
||||
mv.setMemberProtectedDerivedSettable(a8);
|
||||
AssignPrivateDerivedSettable a9 = mv.getMemberPrivateDerivedSettable();
|
||||
mv.setMemberPrivateDerivedSettable(a9);
|
||||
AssignPublicDerivedSettable a7 = imv.getMemberPublicDerivedSettable();
|
||||
imv.setMemberPublicDerivedSettable(a7);
|
||||
AssignProtectedDerivedSettable a8 = imv.getMemberProtectedDerivedSettable();
|
||||
imv.setMemberProtectedDerivedSettable(a8);
|
||||
AssignPrivateDerivedSettable a9 = imv.getMemberPrivateDerivedSettable();
|
||||
imv.setMemberPrivateDerivedSettable(a9);
|
||||
|
||||
|
||||
// (3) Test indirectly non-assignable member variables via classes that themselves have non-assignable member variables
|
||||
MembersMemberVars m = new MembersMemberVars();
|
||||
|
||||
// These will only have getters
|
||||
MemberPublicVar mpv1 = m.getMemberPublic();
|
||||
MemberProtectedVar mpv2 = m.getMemberProtected();
|
||||
MemberPrivateVar mpv3 = m.getMemberPrivate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,3 +34,30 @@ private:
|
|||
|
||||
DeletedBits2<int, double> deleted_bits2;
|
||||
%}
|
||||
|
||||
|
||||
// https://github.com/swig/swig/issues/1416
|
||||
%inline %{
|
||||
template<class T> class AssignTestTemplate {
|
||||
public:
|
||||
AssignTestTemplate() {}
|
||||
T assigntesttemplate;
|
||||
};
|
||||
|
||||
class AssignTestType {
|
||||
private:
|
||||
AssignTestType( const AssignTestType& );
|
||||
AssignTestType& operator=( const AssignTestType& );
|
||||
public:
|
||||
AssignTestType() {}
|
||||
};
|
||||
%}
|
||||
|
||||
%template(AssignTestTmpl) AssignTestTemplate<AssignTestType>;
|
||||
|
||||
%inline %{
|
||||
class AssignTestContainer {
|
||||
public:
|
||||
AssignTestTemplate<AssignTestType> assigntestcontainer;
|
||||
};
|
||||
%}
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
*
|
||||
* Once the analysis is complete, the non-explicit/implied default constructors
|
||||
* and destructors are added to the parse tree. Implied copy constructors are
|
||||
* added too if requested via the copyctor feature.
|
||||
* added too if requested via the copyctor feature. Detection of implied
|
||||
* assignment operators is also handled as assigment is required in the generated
|
||||
* code for variable setters.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
#include "swigmod.h"
|
||||
|
@ -665,6 +667,27 @@ class Allocate:public Dispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
bool is_assignable(Node *n) {
|
||||
bool assignable = true;
|
||||
SwigType *type = Getattr(n, "type");
|
||||
Node *cn = 0;
|
||||
SwigType *ftd = SwigType_typedef_resolve_all(type);
|
||||
SwigType *td = SwigType_strip_qualifiers(ftd);
|
||||
if (SwigType_type(td) == T_USER) {
|
||||
cn = Swig_symbol_clookup(td, 0);
|
||||
if (cn) {
|
||||
if ((Strcmp(nodeType(cn), "class") == 0)) {
|
||||
if (Getattr(cn, "allocate:noassign")) {
|
||||
assignable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Delete(ftd);
|
||||
Delete(td);
|
||||
return assignable;
|
||||
}
|
||||
|
||||
public:
|
||||
Allocate():
|
||||
inclass(NULL), extendmode(0) {
|
||||
|
@ -789,6 +812,7 @@ Allocate():
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Getattr(n, "allocate:has_copy_constructor")) {
|
||||
if (Getattr(n, "abstracts")) {
|
||||
Delattr(n, "allocate:copy_constructor");
|
||||
|
@ -859,6 +883,10 @@ Allocate():
|
|||
allows_assign = 0;
|
||||
}
|
||||
}
|
||||
/* If any member variables are non assignable, this class is also non assignable by default */
|
||||
if (GetFlag(n, "allocate:has_nonassignable")) {
|
||||
allows_assign = 0;
|
||||
}
|
||||
if (!allows_assign) {
|
||||
Setattr(n, "allocate:noassign", "1");
|
||||
}
|
||||
|
@ -1124,6 +1152,10 @@ Allocate():
|
|||
Setattr(n, "cplus:staticbase", inclass);
|
||||
} else if (Cmp(Getattr(n, "kind"), "variable") == 0) {
|
||||
/* Check member variable to determine whether assignment is valid */
|
||||
if (!is_assignable(n)) {
|
||||
SetFlag(n, "feature:immutable");
|
||||
SetFlag(inclass, "allocate:has_nonassignable");
|
||||
}
|
||||
if (SwigType_isreference(Getattr(n, "type"))) {
|
||||
/* Can't assign a class with reference member data */
|
||||
Setattr(inclass, "allocate:noassign", "1");
|
||||
|
@ -1203,6 +1235,11 @@ Allocate():
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Cmp(Getattr(n, "kind"), "variable") == 0) {
|
||||
if (!is_assignable(n))
|
||||
SetFlag(n, "feature:immutable");
|
||||
}
|
||||
}
|
||||
return SWIG_OK;
|
||||
}
|
||||
|
|
|
@ -3736,27 +3736,7 @@ void Language::setOverloadResolutionTemplates(String *argc, String *argv) {
|
|||
}
|
||||
|
||||
int Language::is_assignable(Node *n) {
|
||||
if (GetFlag(n, "feature:immutable"))
|
||||
return 0;
|
||||
int assignable = 1;
|
||||
SwigType *type = Getattr(n, "type");
|
||||
Node *cn = 0;
|
||||
SwigType *ftd = SwigType_typedef_resolve_all(type);
|
||||
SwigType *td = SwigType_strip_qualifiers(ftd);
|
||||
if (SwigType_type(td) == T_USER) {
|
||||
cn = Swig_symbol_clookup(td, 0);
|
||||
if (cn) {
|
||||
if ((Strcmp(nodeType(cn), "class") == 0)) {
|
||||
if (Getattr(cn, "allocate:noassign")) {
|
||||
SetFlag(n, "feature:immutable");
|
||||
assignable = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Delete(ftd);
|
||||
Delete(td);
|
||||
return assignable;
|
||||
return !GetFlag(n, "feature:immutable");
|
||||
}
|
||||
|
||||
String *Language::runtimeCode() {
|
||||
|
|
Loading…
Reference in New Issue