Browse Source

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].
rdb 4 years ago
parent
commit
18e8918138

+ 14 - 49
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);

+ 1 - 0
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 },

+ 32 - 0
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);
+          }
+        }
+      }
     }
   }
 }

+ 1 - 0
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;