Repository: numba/numba

Issue:
math.trunc tests fail under Windows
This may follow from my changes to the trunc implementation,

```
======================================================================
ERROR: test_trunc (__main__.TestMathLib)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\t\numba\numba\tests\test_mathlib.py", line 425, in test_trunc
    self.run_unary(pyfunc, x_types, x_values, flags)
  File "c:\t\numba\numba\tests\test_mathlib.py", line 159, in run_unary
    cr = compile_isolated(pyfunc, [tx], flags=flags)
  File "numba\compiler.py", line 61, in compile_isolated
    locals)
  File "numba\compiler.py", line 94, in compile_extra
    return_type, flags, locals)
  File "numba\compiler.py", line 164, in compile_bytecode
    flags.no_compile)
  File "numba\compiler.py", line 301, in native_lowering_stage
    lower.lower()
  File "numba\lowering.py", line 218, in lower
    self.lower_block(block)
  File "numba\lowering.py", line 243, in lower_block
    raise LoweringError(msg, inst.loc)
LoweringError: Internal error:
Exception: No definition for lowering <built-in function trunc>(int32,) -> int32
File "numba\tests\test_mathlib.py", line 111

======================================================================
ERROR: test_trunc_npm (__main__.TestMathLib)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\t\numba\numba\tests\test_mathlib.py", line 428, in test_trunc_npm
    self.test_trunc(flags=no_pyobj_flags)
  File "c:\t\numba\numba\tests\test_mathlib.py", line 425, in test_trunc
    self.run_unary(pyfunc, x_types, x_values, flags)
  File "c:\t\numba\numba\tests\test_mathlib.py", line 159, in run_unary
    cr = compile_isolated(pyfunc, [tx], flags=flags)
  File "numba\compiler.py", line 61, in compile_isolated
    locals)
  File "numba\compiler.py", line 94, in compile_extra
    return_type, flags, locals)
  File "numba\compiler.py", line 164, in compile_bytecode
    flags.no_compile)
  File "numba\compiler.py", line 301, in native_lowering_stage
    lower.lower()
  File "numba\lowering.py", line 218, in lower
    self.lower_block(block)
  File "numba\lowering.py", line 243, in lower_block
    raise LoweringError(msg, inst.loc)
LoweringError: Internal error:
Exception: No definition for lowering <built-in function trunc>(int32,) -> int32
File "numba\tests\test_mathlib.py", line 111
```



Discussion:
Note this is under 2.7, under 3.x the same failures should show up for ceil() and floor().


Resolving patch:
diff --git a/numba/targets/mathimpl.py b/numba/targets/mathimpl.py
--- a/numba/targets/mathimpl.py
+++ b/numba/targets/mathimpl.py
@@ -16,23 +16,27 @@
 register = registry.register
 
 
-def unary_math_int_impl(fn, f64impl):
-    @register
-    @implement(fn, types.int64)
-    def s64impl(context, builder, sig, args):
+def _unary_int_input_wrapper_impl(wrapped_impl):
+    """
+    Return an implementation factory to convert the single integral input
+    argument to a float, then defer to the *wrapped_impl*.
+    """
+    def implementer(context, builder, sig, args):
         [val] = args
-        fpval = builder.sitofp(val, Type.double())
+        input_type = sig.args[0]
+        if input_type.signed:
+            fpval = builder.sitofp(val, Type.double())
+        else:
+            fpval = builder.uitofp(val, Type.double())
         sig = signature(types.float64, types.float64)
-        return f64impl(context, builder, sig, [fpval])
+        return wrapped_impl(context, builder, sig, [fpval])
 
-    @register
-    @implement(fn, types.uint64)
-    def u64impl(context, builder, sig, args):
-        [val] = args
-        fpval = builder.uitofp(val, Type.double())
-        sig = signature(types.float64, types.float64)
-        return f64impl(context, builder, sig, [fpval])
+    return implementer
 
+def unary_math_int_impl(fn, f64impl):
+    impl = _unary_int_input_wrapper_impl(f64impl)
+    for input_type in [types.intp, types.uintp, types.int64, types.uint64]:
+        register(implement(fn, input_type)(impl))
 
 def unary_math_intr(fn, intrcode):
     @register
@@ -56,30 +60,49 @@ def f64impl(context, builder, sig, args):
     unary_math_int_impl(fn, f64impl)
 
 
-def unary_math_extern(fn, f32extern, f64extern, restype=None):
-
-    def float_input_impl(extern_func, input_type):
-        def implementer(context, builder, sig, args):
-            [val] = args
-            mod = cgutils.get_module(builder)
-            lty = context.get_value_type(input_type)
-            fnty = Type.function(lty, [lty])
-            fn = mod.get_or_insert_function(fnty, name=extern_func)
-            res = builder.call(fn, (val,))
-            if restype is None:
-                return res
-            else:
-                return context.cast(builder, res, input_type,
-                                    restype)
-
-        return implementer
-
-    register(implement(fn, types.float32)
-             (float_input_impl(f32extern, types.float32)))
-    register(implement(fn, types.float64)
-             (float_input_impl(f64extern, types.float64)))
-
-    unary_math_int_impl(fn, float_input_impl(f64extern, types.float64))
+def _float_input_unary_math_extern_impl(extern_func, input_type, restype=None):
+    """
+    Return an implementation factory to call unary *extern_func* with the
+    given argument *input_type*.
+    """
+    def implementer(context, builder, sig, args):
+        [val] = args
+        mod = cgutils.get_module(builder)
+        lty = context.get_value_type(input_type)
+        fnty = Type.function(lty, [lty])
+        fn = mod.get_or_insert_function(fnty, name=extern_func)
+        res = builder.call(fn, (val,))
+        if restype is None:
+            return res
+        else:
+            return context.cast(builder, res, input_type,
+                                restype)
+
+    return implementer
+
+def unary_math_extern(fn, f32extern, f64extern, int_restype=False):
+    """
+    Register implementations of Python function *fn* using the
+    external function named *f32extern* and *f64extern* (for float32
+    and float64 inputs, respectively).
+    If *int_restype* is true, then the function's return value should be
+    integral, otherwise floating-point.
+    """
+    f_restype = types.int64 if int_restype else None
+    f32impl = _float_input_unary_math_extern_impl(f32extern, types.float32, f_restype)
+    f64impl = _float_input_unary_math_extern_impl(f64extern, types.float64, f_restype)
+    register(implement(fn, types.float32)(f32impl))
+    register(implement(fn, types.float64)(f64impl))
+    
+    if int_restype:
+        # If asked for an integral return type, we choose the input type
+        # as the return type.
+        for input_type in [types.intp, types.uintp, types.int64, types.uint64]:
+            impl = _unary_int_input_wrapper_impl(
+                _float_input_unary_math_extern_impl(f64extern, types.float64, input_type))
+            register(implement(fn, input_type)(impl))
+    else:
+        unary_math_int_impl(fn, f64impl)
 
 
 unary_math_intr(math.fabs, lc.INTR_FABS)
@@ -107,13 +130,13 @@ def implementer(context, builder, sig, args):
 unary_math_extern(math.tanh, "tanhf", "tanh")
 # math.floor and math.ceil return float on 2.x, int on 3.x
 if utils.PYVERSION > (3, 0):
-    unary_math_extern(math.ceil, "ceilf", "ceil", types.int64)
-    unary_math_extern(math.floor, "floorf", "floor", types.int64)
+    unary_math_extern(math.ceil, "ceilf", "ceil", True)
+    unary_math_extern(math.floor, "floorf", "floor", True)
 else:
     unary_math_extern(math.ceil, "ceilf", "ceil")
     unary_math_extern(math.floor, "floorf", "floor")
 unary_math_extern(math.sqrt, "sqrtf", "sqrt")
-unary_math_extern(math.trunc, "truncf", "trunc", types.int64)
+unary_math_extern(math.trunc, "truncf", "trunc", True)
 
 
 @register


Test patch: