Repository: numba/numba

Issue:
Invalid closure handling in _create_function_from_code_obj
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->



- [x] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/main/CHANGE_LOG).
- [x] I have included a self contained code sample to reproduce the problem.
  i.e. it's possible to run as 'python bug.py'.

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->

Closure handling was updated as part of Python 3.11 upgrade to include [this line](https://github.com/numba/numba/blob/9f74128def1ca51deebfadecc663f0b80f641c57/numba/core/ir_utils.py#L1743) (`func_arg = func_arg.replace('.', '_')`), which replaces dots in function argument signature (to fix generated argument names I assume). However, this is invalid for default values that are float constants as demonstrated below (removed `InlineClosureLikes` to go through `MakeFunctionToJitFunction` code path which passes default values).

The commit that made this change ([here](https://github.com/numba/numba/commit/77c2672e22be8cb33e47f44884385fd75906881c)), mentions fixing `numba.tests.test_looplifting.TestLoopLiftingInAction.test_lift_listcomp_block0` but the test works with or without this change. Seems like the motivating problem may have been solved in the upstream calls and we can just remove the `func_arg = func_arg.replace('.', '_')` line. Is this correct?

```python
import numba
from numba.core.compiler import DefaultPassBuilder
from numba.core.untyped_passes import InlineClosureLikes

class TestPipeline(numba.core.compiler.CompilerBase):
    def define_pipelines(self):
        pm = DefaultPassBuilder.define_nopython_pipeline(self.state, "TestPipeline")
        for idx, (x, _) in enumerate(pm.passes):
            if x == InlineClosureLikes:
                break
        else:
            raise Exception("Could not find pass %s" % location)
        pm.passes.pop(idx)
        pm.finalize()
        return [pm]

@numba.njit(pipeline_class=TestPipeline)
def f(a):
    def g(x, y=1.1):
        return x+y
    return g(a)

# prints 15 instead of 5.1
print(f(4))
```




Discussion:

Resolving patch:
diff --git a/numba/core/ir_utils.py b/numba/core/ir_utils.py
--- a/numba/core/ir_utils.py
+++ b/numba/core/ir_utils.py
@@ -1739,8 +1739,6 @@ def _create_function_from_code_obj(fcode, func_env, func_arg, func_clo, glbls):
     * func_clo - string for the closure args
     * glbls - the function globals
     """
-    # in py3.11, func_arg contains '.'
-    func_arg = func_arg.replace('.', '_')
     sanitized_co_name = fcode.co_name.replace('<', '_').replace('>', '_')
     func_text = (f"def closure():\n{func_env}\n"
                  f"\tdef {sanitized_co_name}({func_arg}):\n"


Test patch:
diff --git a/numba/tests/test_closure.py b/numba/tests/test_closure.py
--- a/numba/tests/test_closure.py
+++ b/numba/tests/test_closure.py
@@ -473,6 +473,24 @@ def bar(z):
             regex = r'closure__locals__bar_v[0-9]+.x'
             self.assertRegex(name, regex)
 
+    def test_issue9222(self):
+        # Ensures that float default arguments are handled correctly in
+        # closures.
+
+        @njit
+        def foo():
+            def bar(x, y=1.1):
+                return x + y
+            return bar
+
+        @njit
+        def consume():
+            return foo()(4)
+
+        # In Issue #9222, the result was completely wrong - 15 instead of 5.1 -
+        # so allclose should be sufficient for comparison here.
+        np.testing.assert_allclose(consume(), 4 + 1.1)
+
 
 class TestObjmodeFallback(TestCase):
     # These are all based on tests from real life issues where, predominantly,