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].
This commit is contained in:
rdb 2021-04-25 11:27:18 +02:00
parent 5ef1b44455
commit 18e8918138
4 changed files with 48 additions and 49 deletions

View File

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

View File

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

View File

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

View File

@ -70,6 +70,7 @@ private:
F_setter = 0x0020,
F_unary_op = 0x0040,
F_operator_typecast = 0x0080,
F_item_assignment = 0x0100,
};
int _flags;