From 572b640211e23789ab429e613ebef277f5feafb0 Mon Sep 17 00:00:00 2001 From: da-woods Date: Fri, 21 Jul 2023 08:00:10 +0100 Subject: [PATCH] Improve error diagnostic for exception-spec mismatch (#5547) It seems like the most common Cython 3 snag is the change in function exception behaviour. This PR adds some extra diagnostics to help people navigate this change, and a general mechanism for adding this kind of diagnostics. It needs some tests, but I wasn't to see what tests fail to tell me where to put them. --- Cython/Compiler/ExprNodes.py | 6 +++++- Cython/Compiler/PyrexTypes.py | 21 +++++++++++++++++++++ tests/errors/cfuncptr.pyx | 20 ++++++++++---------- tests/errors/e_excvalfunctype.pyx | 4 ++-- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 275a6233da5..598fa1f25ad 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1077,7 +1077,11 @@ def coerce_to(self, dst_type, env): return src def fail_assignment(self, dst_type): - error(self.pos, "Cannot assign type '%s' to '%s'" % (self.type, dst_type)) + extra_diagnostics = dst_type.assignment_failure_extra_info(self.type) + if extra_diagnostics: + extra_diagnostics = ". " + extra_diagnostics + error(self.pos, "Cannot assign type '%s' to '%s'%s" % ( + self.type, dst_type, extra_diagnostics)) def check_for_coercion_error(self, dst_type, env, fail=False, default=None): if fail and not default: diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 73e358d9de5..540ed73626d 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -318,6 +318,11 @@ def assignable_from(self, src_type): def assignable_from_resolved_type(self, src_type): return self.same_as(src_type) + def assignment_failure_extra_info(self, src_type): + """Override if you can useful provide extra + information about why an assignment didn't work.""" + return "" + def as_argument_type(self): return self @@ -2860,6 +2865,22 @@ def assignable_from_resolved_type(self, other_type): return self.base_type.is_void or self.base_type.same_as(other_type.base_type) return 0 + def assignment_failure_extra_info(self, src_type): + if self.base_type.is_cfunction and src_type.is_ptr: + src_type = src_type.base_type.resolve() + if self.base_type.is_cfunction and src_type.is_cfunction: + copied_src_type = copy.copy(src_type) + # make the exception values the same as us + copied_src_type.exception_check = self.base_type.exception_check + copied_src_type.exception_value = self.base_type.exception_value + if self.base_type.pointer_assignable_from_resolved_type(copied_src_type): + # the only reason we can't assign is because of exception incompatibility + msg = "Exception values are incompatible." + if not self.base_type.exception_check and not self.base_type.exception_value: + msg += " Suggest adding 'noexcept' to type '{}'.".format(src_type) + return msg + return super(CPtrType, self).assignment_failure_extra_info(src_type) + def specialize(self, values): base_type = self.base_type.specialize(values) if base_type == self.base_type: diff --git a/tests/errors/cfuncptr.pyx b/tests/errors/cfuncptr.pyx index 9b5f1264481..06fdce9e40e 100644 --- a/tests/errors/cfuncptr.pyx +++ b/tests/errors/cfuncptr.pyx @@ -41,14 +41,14 @@ def fail_struct_pointer(): _ERRORS = """ -13:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except -2' -14:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except -1' -15:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except? -1' -29:13: Cannot assign type 'int (int) except *' to 'int (*)(int) noexcept' -30:13: Cannot assign type 'int (int) except *' to 'int (*)(int) except -1' -31:13: Cannot assign type 'int (int) except *' to 'int (*)(int) except? -1' -40:32: Cannot assign type 'int (*)(int) except? -1 nogil' to 'int (*)(int) noexcept nogil' -40:32: Cannot assign type 'int (*)(int) except? -1 nogil' to 'int (*)(int) noexcept nogil' -40:37: Cannot assign type 'void (*)(int) except * nogil' to 'void (*)(int) noexcept nogil' -40:37: Cannot assign type 'void (*)(int) except * nogil' to 'void (*)(int) noexcept nogil' +13:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except -2'. Exception values are incompatible. +14:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except -1'. Exception values are incompatible. +15:13: Cannot assign type 'int (int) except? -2' to 'int (*)(int) except? -1'. Exception values are incompatible. +29:13: Cannot assign type 'int (int) except *' to 'int (*)(int) noexcept'. Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int) except *'. +30:13: Cannot assign type 'int (int) except *' to 'int (*)(int) except -1'. Exception values are incompatible. +31:13: Cannot assign type 'int (int) except *' to 'int (*)(int) except? -1'. Exception values are incompatible. +40:32: Cannot assign type 'int (*)(int) except? -1 nogil' to 'int (*)(int) noexcept nogil'. Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int) except? -1 nogil'. +40:32: Cannot assign type 'int (*)(int) except? -1 nogil' to 'int (*)(int) noexcept nogil'. Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int) except? -1 nogil'. +40:37: Cannot assign type 'void (*)(int) except * nogil' to 'void (*)(int) noexcept nogil'. Exception values are incompatible. Suggest adding 'noexcept' to type 'void (int) except * nogil'. +40:37: Cannot assign type 'void (*)(int) except * nogil' to 'void (*)(int) noexcept nogil'. Exception values are incompatible. Suggest adding 'noexcept' to type 'void (int) except * nogil'. """ diff --git a/tests/errors/e_excvalfunctype.pyx b/tests/errors/e_excvalfunctype.pyx index 25cae47c614..fb5b4c7ce67 100644 --- a/tests/errors/e_excvalfunctype.pyx +++ b/tests/errors/e_excvalfunctype.pyx @@ -11,6 +11,6 @@ spam = grail # type mismatch _ERRORS = u""" -9:8: Cannot assign type 'spamfunc' to 'grailfunc' -10:7: Cannot assign type 'grailfunc' to 'spamfunc' +9:8: Cannot assign type 'spamfunc' to 'grailfunc'. Exception values are incompatible. Suggest adding 'noexcept' to type 'int (int, char *) except 42'. +10:7: Cannot assign type 'grailfunc' to 'spamfunc'. Exception values are incompatible. """