From 020cec980e56a3048e16844d4751ad0b4418b164 Mon Sep 17 00:00:00 2001
From: Xavier Thompson <xavier.thompson@nexedi.com>
Date: Tue, 28 Jul 2020 17:42:13 +0200
Subject: [PATCH] Disallow cypclass method overloading with narrower or larger
 arguments

---
 Cython/Compiler/PyrexTypes.py | 26 ++++++++++++++++++++++++++
 Cython/Compiler/Symtab.py     |  7 ++++---
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
index c32d8a229..a7961a751 100644
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -3044,6 +3044,32 @@ class CFuncType(CType):
                 return 0
         return 1
 
+    def narrower_or_larger_arguments_than(self, other_type, as_cmethod = 0):
+        return self.narrower_or_larger_arguments_than_resolved_type(other_type.resolve(), as_cmethod)
+
+    def narrower_or_larger_arguments_than_resolved_type(self, other_type, as_cmethod):
+        if other_type is error_type:
+            return 1
+        if not other_type.is_cfunction:
+            return 0
+        nargs = len(self.args)
+        if nargs != len(other_type.args):
+            return 0
+        for i in range(as_cmethod, nargs):
+            if not self.args[i].type.subtype_of_resolved_type(other_type.args[i].type):
+                if not other_type.args[i].type.subtype_of_resolved_type(self.args[i].type):
+                    return 0
+                else:
+                    other_type.args[i].needs_type_test = True
+            else:
+                self.args[i].needs_type_test = other_type.args[i].needs_type_test \
+                        or not self.args[i].type.same_as(other_type.args[i].type)
+        if self.has_varargs != other_type.has_varargs:
+            return 0
+        if self.optional_arg_count != other_type.optional_arg_count:
+            return 0
+        return 1
+
     def narrower_arguments_than(self, other_type, as_cmethod = 0):
         return self.narrower_arguments_than_resolved_type(other_type.resolve(), as_cmethod)
 
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
index 2497510f4..a6cf943a6 100644
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -571,7 +571,7 @@ class Scope(object):
                                 alt_declarator_str = alt_type.declarator_code(name, for_display = 1).strip()
                                 new_declarator_str = type.declarator_code(name, for_display = 1).strip()
                                 if new_declarator_str != alt_declarator_str:
-                                    error(pos, ("Fallacious override:\n"
+                                    error(pos, ("False override:\n"
                                                 "Cypclass method\n"
                                                 ">>     %s\n"
                                                 "has compatible arguments with inherited method\n"
@@ -598,8 +598,9 @@ class Scope(object):
 
                         # if an overloaded alternative has narrower argument types than another, then the method
                         # actually called will depend on the static type of the arguments
-                        elif type.narrower_arguments_than(alt_type) or alt_type.narrower_arguments_than(type):
-                            error(pos, "Cypclass overloaded method with narrower arguments")
+                        # we actually also disallow methods where each argument is either narrower or larger
+                        elif type.narrower_or_larger_arguments_than(alt_type):
+                            error(pos, "Cypclass overloaded method with narrower or larger arguments")
                             if alt_entry.pos is not None:
                                 error(alt_entry.pos, "Conflicting method is defined here")
 
-- 
2.30.9