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:
William S Fulton 2023-09-06 19:18:16 +01:00
parent dad1cca849
commit e07957ad4c
7 changed files with 138 additions and 37 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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