Explorar o código

interrogate: Support three-way comparison operator <=>

rdb %!s(int64=4) %!d(string=hai) anos
pai
achega
f6b1b6dbb0

+ 5 - 0
dtool/src/interrogate/functionRemap.cxx

@@ -738,6 +738,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
       _void_return = true;
     }
 
+  } else if (fname == "operator <=>") {
+    // This returns an opaque object that we must leave unchanged.
+    _return_type = new ParameterRemapUnchanged(rtype);
+    _void_return = false;
+
   } else {
     // The normal case.
     _return_type = interface_maker->remap_parameter(_cpptype, rtype);

+ 46 - 6
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -70,6 +70,7 @@ RenameSet methodRenameDictionary[] = {
   { "operator >"    , "__gt__",                 0 },
   { "operator <="   , "__le__",                 0 },
   { "operator >="   , "__ge__",                 0 },
+  { "operator <=>"  , "__cmp__",                0 },
   { "operator ="    , "assign",                 0 },
   { "operator ()"   , "__call__",               0 },
   { "operator []"   , "__getitem__",            0 },
@@ -1753,7 +1754,8 @@ write_module_class(ostream &out, Object *obj) {
           fname == "operator ==" ||
           fname == "operator !=" ||
           fname == "operator >" ||
-          fname == "operator >=") {
+          fname == "operator >=" ||
+          fname == "operator <=>") {
         continue;
       }
 
@@ -2620,6 +2622,7 @@ write_module_class(ostream &out, Object *obj) {
     out << "    return nullptr;\n";
     out << "  }\n\n";
 
+    std::set<FunctionRemap *> threeway_remaps;
     bool have_eq = false;
     bool have_ne = false;
     for (Function *func : obj->_methods) {
@@ -2650,6 +2653,9 @@ write_module_class(ostream &out, Object *obj) {
         op_type = "Py_GT";
       } else if (fname == "operator >=") {
         op_type = "Py_GE";
+      } else if (fname == "operator <=>") {
+        threeway_remaps = std::move(remaps);
+        continue;
       } else {
         continue;
       }
@@ -2670,7 +2676,15 @@ write_module_class(ostream &out, Object *obj) {
     }
 
     if (has_local_richcompare) {
-      if (have_eq && !have_ne) {
+      if (!threeway_remaps.empty()) {
+        out << "  default:\n";
+        out << "    {\n";
+        string expected_params;
+        write_function_forset(out, threeway_remaps, 1, 1, expected_params, 6, true, false,
+                              AT_single_arg, RF_richcompare_zero | RF_err_null, false);
+        out << "    }\n";
+      }
+      else if (have_eq && !have_ne) {
         // Generate a not-equal function from the equal function.
         for (Function *func : obj->_methods) {
           std::set<FunctionRemap*> remaps;
@@ -2713,8 +2727,13 @@ write_module_class(ostream &out, Object *obj) {
       out << "    PyErr_Clear();\n";
       out << "  }\n\n";
     }
+    else if (!threeway_remaps.empty()) {
+      string expected_params;
+      write_function_forset(out, threeway_remaps, 1, 1, expected_params, 2, true, false,
+                            AT_single_arg, RF_richcompare_zero | RF_err_null, false);
+    }
 
-    if (slots.count("tp_compare")) {
+    if (slots.count("tp_compare") && threeway_remaps.empty()) {
       // A lot of Panda code depends on comparisons being done via the
       // compare_to function, which is mapped to the tp_compare slot, which
       // Python 3 no longer has.  So, we'll write code to fall back to that if
@@ -3536,7 +3555,8 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
       fname == "operator ==" ||
       fname == "operator !=" ||
       fname == "operator >" ||
-      fname == "operator >=") {
+      fname == "operator >=" ||
+      fname == "operator <=>") {
     return;
   }
 
@@ -6153,7 +6173,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     // function call, so it should reduce the amount of code output while not
     // being any slower.
     bool return_null = (return_flags & RF_pyobject) != 0 &&
-                       (return_flags & RF_err_null) != 0;
+                       (return_flags & RF_err_null) != 0 &&
+                       (return_flags & RF_richcompare_zero) == 0;
     if (return_null && return_expr.empty()) {
       indent(out, indent_level)
         << "return Dtool_Return_None();\n";
@@ -6306,6 +6327,10 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     indent(out, indent_level) << "Py_INCREF(self);\n";
     indent(out, indent_level) << "return self;\n";
 
+  } else if (return_flags & RF_richcompare_zero) {
+    indent(out, indent_level)
+      << "Py_RETURN_RICHCOMPARE(" << return_expr << ", 0, op);\n";
+
   } else if (return_flags & RF_pyobject) {
     if (return_expr.empty()) {
       indent(out, indent_level) << "Py_INCREF(Py_None);\n";
@@ -7650,7 +7675,11 @@ is_remap_legal(FunctionRemap *remap) {
   if (!is_cpp_type_legal(remap->_return_type->get_orig_type())) {
 // printf("  is_remap_legal Return Is Bad %s\n",remap->_return_type->get_orig_
 // type()->get_fully_scoped_name().c_str());
-    return false;
+    // Except if this is a spaceship operator, since we have special handling
+    // for its return type.
+    if (remap->_cppfunc->get_simple_name() != "operator <=>") {
+      return false;
+    }
   }
 
   // We don't currently support returning pointers, but we accept them as
@@ -8109,6 +8138,17 @@ NeedsARichCompareFunction(const InterrogateType &itype_class) {
     }
   }
 
+  if (itype_class._cpptype != nullptr) {
+    CPPStructType *struct_type = itype_class._cpptype->as_struct_type();
+    if (struct_type != nullptr) {
+      CPPScope *scope = struct_type->get_scope();
+      CPPScope::Functions::const_iterator it = scope->_functions.find("operator <=>");
+      if (it != scope->_functions.end()) {
+        return true;
+      }
+    }
+  }
+
   return false;
 }
 

+ 3 - 0
dtool/src/interrogate/interfaceMakerPythonNative.h

@@ -118,6 +118,9 @@ private:
 
     // Invert boolean return value.
     RF_invert_bool = 0x8000,
+
+    // Used inside a rich comparison function.
+    RF_richcompare_zero = 0x10000,
   };
 
   class SlottedFunctionDef {