From 18e8918138f185619fe68133b5df21d95d5e316b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 25 Apr 2021 11:27:18 +0200 Subject: [PATCH] interrogate: Reimplement subscript assignment "operator" This redoes the handling of `obj[n] = item` to occur in InterrogateBuilder instead so that it doesn't prevent the same remap from being used as getitem. This fixes the non-const variant of InstanceList[n]. --- dtool/src/interrogate/functionRemap.cxx | 63 +++++-------------- .../interfaceMakerPythonNative.cxx | 1 + dtool/src/interrogate/interrogateBuilder.cxx | 32 ++++++++++ dtool/src/interrogatedb/interrogateFunction.h | 1 + 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index 538449255b..a3272b2c70 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -419,6 +419,17 @@ get_call_str(const string &container, const vector_string &pexprs) const { call << ')'; } + } else if (_type == T_item_assignment_operator) { + call << "("; + _parameters[0]._remap->pass_parameter(call, container); + call << ")["; + + size_t pn = _first_true_parameter; + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + call << "] = "; + ++pn; + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + } else { const char *separator = ""; @@ -468,11 +479,6 @@ get_call_str(const string &container, const vector_string &pexprs) const { size_t pn = _first_true_parameter; size_t num_parameters = pexprs.size(); - if (_type == T_item_assignment_operator) { - // The last parameter is the value to set. - --num_parameters; - } - for (pn = _first_true_parameter; pn < num_parameters; ++pn) { nassertd(pn < _parameters.size()) break; @@ -481,11 +487,6 @@ get_call_str(const string &container, const vector_string &pexprs) const { separator = ", "; } call << ")"; - - if (_type == T_item_assignment_operator) { - call << " = "; - _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); - } } return call.str(); @@ -568,6 +569,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } else if ((ifunc._flags & InterrogateFunction::F_setter) != 0) { _type = T_setter; + + } else if ((ifunc._flags & InterrogateFunction::F_item_assignment) != 0) { + _type = T_item_assignment_operator; } if ((_cppfunc->_storage_class & CPPInstance::SC_blocking) != 0) { @@ -623,14 +627,6 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak fname == "operator <<=" || fname == "operator >>=") { _type = T_assignment_method; - - } else if (fname == "operator []" && !_const_method && rtype != nullptr) { - // Check if this is an item-assignment operator. - CPPReferenceType *reftype = rtype->as_reference_type(); - if (reftype != nullptr && reftype->_pointing_at->as_const_type() == nullptr) { - // It returns a mutable reference. - _type = T_item_assignment_operator; - } } } @@ -707,37 +703,6 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } } - } else if (_type == T_item_assignment_operator) { - // An item-assignment method isn't really a thing in C++, but it is in - // scripting languages, so we use this to denote item-access operators - // that return a non-const reference. - - if (_cpptype == nullptr) { - nout << "Method " << *_cppfunc << " has no struct type\n"; - return false; - } else { - // Synthesize a const reference parameter for the assignment. - CPPType *bare_type = TypeManager::unwrap_reference(rtype); - CPPType *const_type = CPPType::new_type(new CPPConstType(bare_type)); - CPPType *ref_type = CPPType::new_type(new CPPReferenceType(const_type)); - - Parameter param; - param._has_name = true; - param._name = "assign_val"; - param._remap = interface_maker->remap_parameter(_cpptype, ref_type); - - if (param._remap == nullptr || !param._remap->is_valid()) { - nout << "Invalid remap for assignment type of method " << *_cppfunc << "\n"; - return false; - } - _parameters.push_back(param); - - // Pretend we don't return anything at all. - CPPType *void_type = TypeManager::get_void_type(); - _return_type = interface_maker->remap_parameter(_cpptype, void_type); - _void_return = true; - } - } else if (fname == "operator <=>") { // This returns an opaque object that we must leave unchanged. _return_type = new ParameterRemapUnchanged(rtype); diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index d1d9765524..3287903f59 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -74,6 +74,7 @@ RenameSet methodRenameDictionary[] = { { "operator =" , "assign", 0 }, { "operator ()" , "__call__", 0 }, { "operator []" , "__getitem__", 0 }, + { "operator [] =" , "__setitem__", 0 }, { "operator ++unary", "increment", 0 }, { "operator ++" , "increment", 0 }, { "operator --unary", "decrement", 0 }, diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 618fc11fdc..e94d4bda39 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -3012,6 +3012,38 @@ define_method(CPPInstance *function, InterrogateType &itype, index) == itype._methods.end()) { itype._methods.push_back(index); } + + // For an operator [] returning a non-const reference, we synthesize an + // "item-assignment" operator, which does not exist in C++ but does in + // scripting languages. This allows `obj[n] = ...` + if (ftype->_return_type != nullptr && + ftype->_return_type->is_reference() && + !ftype->_return_type->remove_reference()->is_const() && + (ftype->_flags & CPPFunctionType::F_const_method) == 0 && + function->get_simple_name() == "operator []") { + + // Make up a CPPFunctionType with extra parameter. + CPPType *assign_type = TypeManager::wrap_const_reference(ftype->_return_type->remove_reference()); + CPPParameterList *params = new CPPParameterList(*(ftype->_parameters)); + CPPInstance *param1 = new CPPInstance(assign_type, "assign_val"); + params->_parameters.push_back(param1); + CPPType *void_type = TypeManager::get_void_type(); + CPPFunctionType *ftype = new CPPFunctionType(void_type, params, 0); + + // Now make up an instance for the function. + CPPInstance *function = new CPPInstance(ftype, "operator [] ="); + function->_ident->_native_scope = scope; + + FunctionIndex index = get_function(function, "", + struct_type, scope, + InterrogateFunction::F_item_assignment); + if (index != 0) { + if (find(itype._methods.begin(), itype._methods.end(), + index) == itype._methods.end()) { + itype._methods.push_back(index); + } + } + } } } } diff --git a/dtool/src/interrogatedb/interrogateFunction.h b/dtool/src/interrogatedb/interrogateFunction.h index 1e3a251032..e89e76b80c 100644 --- a/dtool/src/interrogatedb/interrogateFunction.h +++ b/dtool/src/interrogatedb/interrogateFunction.h @@ -70,6 +70,7 @@ private: F_setter = 0x0020, F_unary_op = 0x0040, F_operator_typecast = 0x0080, + F_item_assignment = 0x0100, }; int _flags;