def preprocess_fields(self, fields): # temp name to allow Optional instantiation self.name = f"numba.PolygonStructType#{id(self)}" fields = tuple([ ('value', types.Optional(types.int64)), ('parent', types.Optional(self)), ]) return fields
def _list_getitem_pop_helper(typingctx, l, index, op): """Wrap numba_list_getitem and numba_list_pop Returns 2-tuple of (int32, ?item_type) This is a helper that is parametrized on the type of operation, which can be either 'pop' or 'getitem'. This is because, signature wise, getitem and pop and are the same. """ assert (op in ("pop", "getitem")) IS_NOT_NONE = not isinstance(l.item_type, types.NoneType) resty = types.Tuple([ types.int32, types.Optional(l.item_type if IS_NOT_NONE else types.int64) ]) sig = resty(l, index) def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_status, [ll_list_type, ll_ssize_t, ll_bytes], ) [tl, tindex] = sig.args [l, index] = args fn = builder.module.get_or_insert_function( fnty, name='numba_list_{}'.format(op)) dm_item = context.data_model_manager[tl.item_type] ll_item = context.get_data_type(tl.item_type) ptr_item = cgutils.alloca_once(builder, ll_item) lp = _container_get_data(context, builder, tl, l) status = builder.call( fn, [ lp, index, _as_bytes(builder, ptr_item), ], ) # Load item if output is available found = builder.icmp_signed('>=', status, status.type(int(ListStatus.LIST_OK))) out = context.make_optional_none( builder, tl.item_type if IS_NOT_NONE else types.int64) pout = cgutils.alloca_once_value(builder, out) with builder.if_then(found): if IS_NOT_NONE: item = dm_item.load_from_data_pointer(builder, ptr_item) context.nrt.incref(builder, tl.item_type, item) loaded = context.make_optional_value(builder, tl.item_type, item) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [status, out]) return sig, codegen
def test_optional_to_optional(self): """ Test error due mishandling of Optional to Optional casting Related issue: https://github.com/numba/numba/issues/1718 """ # Attempt to cast optional(intp) to optional(float64) opt_int = types.Optional(types.intp) opt_flt = types.Optional(types.float64) sig = opt_flt(opt_int) @njit(sig) def foo(a): return a self.assertEqual(foo(2), 2) self.assertIsNone(foo(None))
def _builtin_infer(self, py_type): # The type hierarchy of python typing library changes in 3.7. generic_type_check = _py_version_switch( (3, 7), lambda x: isinstance(x, py_typing._GenericAlias), lambda _: True, ) if not generic_type_check(py_type): return list_origin = _py_version_switch((3, 7), list, py_typing.List) dict_origin = _py_version_switch((3, 7), dict, py_typing.Dict) set_origin = _py_version_switch((3, 7), set, py_typing.Set) tuple_origin = _py_version_switch((3, 7), tuple, py_typing.Tuple) if getattr(py_type, "__origin__", None) is py_typing.Union: if len(py_type.__args__) != 2: raise errors.TypingError( "Cannot type Union of more than two types") (arg_1_py, arg_2_py) = py_type.__args__ if arg_2_py is type(None): # noqa: E721 return types.Optional(self.infer(arg_1_py)) elif arg_1_py is type(None): # noqa: E721 return types.Optional(self.infer(arg_2_py)) else: raise errors.TypingError( "Cannot type Union that is not an Optional " f"(neither type type {arg_2_py} is not NoneType") if getattr(py_type, "__origin__", None) is list_origin: (element_py, ) = py_type.__args__ return types.ListType(self.infer(element_py)) if getattr(py_type, "__origin__", None) is dict_origin: key_py, value_py = py_type.__args__ return types.DictType(self.infer(key_py), self.infer(value_py)) if getattr(py_type, "__origin__", None) is set_origin: (element_py, ) = py_type.__args__ return types.Set(self.infer(element_py)) if getattr(py_type, "__origin__", None) is tuple_origin: tys = tuple(map(self.infer, py_type.__args__)) return types.BaseTuple.from_types(tys)
def impl(typingctx, l_ty, index_ty): is_none = isinstance(l_ty.item_type, types.NoneType) if is_none: resty = types.Tuple([types.int32, l_ty.item_type]) else: resty = types.Tuple([types.int32, types.Optional(l_ty.item_type)]) sig = resty(l_ty, index_ty) def codegen(context, builder, sig, args): [tl, tindex] = sig.args [l, index] = args fnty = ir.FunctionType( ll_voidptr_type, [ll_list_type], ) fname = 'numba_list_base_ptr' fn = builder.module.get_or_insert_function(fnty, fname) fn.attributes.add('alwaysinline') fn.attributes.add('nounwind') fn.attributes.add('readonly') lp = _container_get_data(context, builder, tl, l) base_ptr = builder.call( fn, [ lp, ], ) llty = context.get_data_type(tl.item_type) casted_base_ptr = builder.bitcast(base_ptr, llty.as_pointer()) item_ptr = cgutils.gep(builder, casted_base_ptr, index) if is_none: out = builder.load(item_ptr) else: out = context.make_optional_none(builder, tl.item_type) pout = cgutils.alloca_once_value(builder, out) dm_item = context.data_model_manager[tl.item_type] item = dm_item.load_from_data_pointer(builder, item_ptr) if not borrowed: context.nrt.incref(builder, tl.item_type, item) if is_none: loaded = item else: loaded = context.make_optional_value( builder, tl.item_type, item) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [ll_status(0), out]) return sig, codegen
def _list_getitem(typingctx, l, index): """Wrap numba_list_getitem Returns 2-tuple of (int32, ?item_type) """ IS_NOT_NONE = not isinstance(l.item_type, types.NoneType) resty = types.Tuple([types.int32, types.Optional(l.item_type if IS_NOT_NONE else types.int64)]) sig = resty(l, index) def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_status, [ll_list_type, ll_ssize_t, ll_bytes], ) [tl, tindex] = sig.args [l, index] = args fn = builder.module.get_or_insert_function( fnty, name='numba_list_getitem') dm_item = context.data_model_manager[tl.item_type] ll_item = context.get_data_type(tl.item_type) ptr_item = cgutils.alloca_once(builder, ll_item) lp = _container_get_data(context, builder, tl, l) status = builder.call( fn, [ lp, index, _as_bytes(builder, ptr_item), ], ) # Load item if output is available found = builder.icmp_signed('>=', status, status.type(int(ListStatus.LIST_OK))) out = context.make_optional_none(builder, tl.item_type if IS_NOT_NONE else types.int64) pout = cgutils.alloca_once_value(builder, out) with builder.if_then(found): if IS_NOT_NONE: item = dm_item.load_from_data_pointer(builder, ptr_item) context.nrt.incref(builder, tl.item_type, item) loaded = context.make_optional_value( builder, tl.item_type, item) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [status, out]) return sig, codegen
def _dict_lookup(typingctx, d, key, hashval): """Wrap numba_dict_lookup Returns 2-tuple of (intp, ?value_type) """ resty = types.Tuple([types.intp, types.Optional(d.value_type)]) sig = resty(d, key, hashval) def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_ssize_t, [ll_dict_type, ll_bytes, ll_hash, ll_bytes], ) [td, tkey, thashval] = sig.args [d, key, hashval] = args fn = cgutils.get_or_insert_function(builder.module, fnty, 'numba_dict_lookup') dm_key = context.data_model_manager[tkey] dm_val = context.data_model_manager[td.value_type] data_key = dm_key.as_data(builder, key) ptr_key = cgutils.alloca_once_value(builder, data_key) cgutils.memset_padding(builder, ptr_key) ll_val = context.get_data_type(td.value_type) ptr_val = cgutils.alloca_once(builder, ll_val) dp = _container_get_data(context, builder, td, d) ix = builder.call( fn, [ dp, _as_bytes(builder, ptr_key), hashval, _as_bytes(builder, ptr_val), ], ) # Load value if output is available found = builder.icmp_signed('>', ix, ix.type(int(DKIX.EMPTY))) out = context.make_optional_none(builder, td.value_type) pout = cgutils.alloca_once_value(builder, out) with builder.if_then(found): val = dm_val.load_from_data_pointer(builder, ptr_val) context.nrt.incref(builder, td.value_type, val) loaded = context.make_optional_value(builder, td.value_type, val) builder.store(loaded, pout) out = builder.load(pout) return context.make_tuple(builder, resty, [ix, out]) return sig, codegen
def test_optional(self): self.assertEqual( as_numba_type(py_typing.Optional[float]), types.Optional(self.float_nb_type), ) self.assertEqual( as_numba_type(py_typing.Union[str, None]), types.Optional(self.str_nb_type), ) self.assertEqual( as_numba_type(py_typing.Union[None, bool]), types.Optional(self.bool_nb_type), ) # Optional[x] is a special case of Union[x, None]. We raise a # TypingError if the right type is not NoneType. with self.assertRaises(TypingError) as raises: as_numba_type(py_typing.Union[int, float]) self.assertIn("Cannot type Union that is not an Optional", str(raises.exception))
def test_optional_tuple(self): # Unify to optional tuple aty = types.none bty = types.UniTuple(i32, 2) self.assert_unify(aty, bty, types.Optional(types.UniTuple(i32, 2))) aty = types.Optional(types.UniTuple(i16, 2)) bty = types.UniTuple(i32, 2) self.assert_unify(aty, bty, types.Optional(types.UniTuple(i32, 2))) # Unify to tuple of optionals aty = types.Tuple((types.none, i32)) bty = types.Tuple((i16, types.none)) self.assert_unify(aty, bty, types.Tuple((types.Optional(i16), types.Optional(i32)))) aty = types.Tuple((types.Optional(i32), i64)) bty = types.Tuple((i16, types.Optional(i8))) self.assert_unify(aty, bty, types.Tuple((types.Optional(i32), types.Optional(i64))))
def test_optional(self): aty = types.int32 bty = types.Optional(i32) self.assert_can_convert(types.none, bty, Conversion.promote) self.assert_can_convert(aty, bty, Conversion.promote) self.assert_cannot_convert(bty, types.none) self.assert_can_convert(bty, aty, Conversion.safe) # XXX ??? # Optional array aty = types.Array(i32, 2, "C") bty = types.Optional(aty) self.assert_can_convert(types.none, bty, Conversion.promote) self.assert_can_convert(aty, bty, Conversion.promote) self.assert_can_convert(bty, aty, Conversion.safe) aty = types.Array(i32, 2, "C") bty = types.Optional(aty.copy(layout="A")) self.assert_can_convert(aty, bty, Conversion.safe) # C -> A self.assert_cannot_convert(bty, aty) # A -> C aty = types.Array(i32, 2, "C") bty = types.Optional(aty.copy(layout="F")) self.assert_cannot_convert(aty, bty) self.assert_cannot_convert(bty, aty)
def test_none_to_optional(self): """ Test unification of `none` and multiple number types to optional type """ ctx = typing.Context() for tys in itertools.combinations(types.number_domain, 2): # First unify without none, to provide the control value tys = list(tys) expected = types.Optional(ctx.unify_types(*tys)) results = [ctx.unify_types(*comb) for comb in itertools.permutations(tys + [types.none])] # All results must be equal for res in results: self.assertEqual(res, expected)
def _dict_popitem(typingctx, d): """Wrap numba_dict_popitem """ keyvalty = types.Tuple([d.key_type, d.value_type]) resty = types.Tuple([types.int32, types.Optional(keyvalty)]) sig = resty(d) def codegen(context, builder, sig, args): fnty = ir.FunctionType( ll_status, [ll_dict_type, ll_bytes, ll_bytes], ) [d] = args [td] = sig.args fn = builder.module.get_or_insert_function(fnty, name='numba_dict_popitem') dm_key = context.data_model_manager[td.key_type] dm_val = context.data_model_manager[td.value_type] ptr_key = cgutils.alloca_once(builder, dm_key.get_data_type()) ptr_val = cgutils.alloca_once(builder, dm_val.get_data_type()) dp = _container_get_data(context, builder, td, d) status = builder.call( fn, [ dp, _as_bytes(builder, ptr_key), _as_bytes(builder, ptr_val), ], ) out = context.make_optional_none(builder, keyvalty) pout = cgutils.alloca_once_value(builder, out) cond = builder.icmp_signed('==', status, status.type(int(Status.OK))) with builder.if_then(cond): key = dm_key.load_from_data_pointer(builder, ptr_key) val = dm_val.load_from_data_pointer(builder, ptr_val) keyval = context.make_tuple(builder, keyvalty, [key, val]) optkeyval = context.make_optional_value(builder, keyvalty, keyval) builder.store(optkeyval, pout) out = builder.load(pout) return cgutils.pack_struct(builder, [status, out]) return sig, codegen
def test_optional(self): aty = types.Optional(i32) bty = types.none self.assert_unify(aty, bty, aty) aty = types.Optional(i32) bty = types.Optional(i64) self.assert_unify(aty, bty, bty) aty = types.Optional(i32) bty = i64 self.assert_unify(aty, bty, types.Optional(i64)) # Failure aty = types.Optional(i32) bty = types.Optional(types.slice3_type) self.assert_unify_failure(aty, bty)
def test_optional_unpack(self): """ Issue 2171 """ def pyfunc(x): if x is None: return else: a, b = x return a, b tup = types.Tuple([types.intp] * 2) opt_tup = types.Optional(tup) sig = (opt_tup, ) cfunc = njit(sig)(pyfunc) self.assertEqual(pyfunc(None), cfunc(None)) self.assertEqual(pyfunc((1, 2)), cfunc((1, 2)))
def test_nested_containers(self): IntList = py_typing.List[int] self.assertEqual( as_numba_type(py_typing.List[IntList]), types.ListType(types.ListType(self.int_nb_type)), ) self.assertEqual( as_numba_type(py_typing.List[py_typing.Dict[float, bool]]), types.ListType( types.DictType(self.float_nb_type, self.bool_nb_type)), ) self.assertEqual( as_numba_type( py_typing.Set[py_typing.Tuple[py_typing.Optional[int], float]]), types.Set( types.Tuple( [types.Optional(self.int_nb_type), self.float_nb_type])), )
def make_optional_value(self, builder, valtype, value): optval = self.make_helper(builder, types.Optional(valtype)) optval.valid = cgutils.true_bit optval.data = value return optval._getvalue()
def make_optional_none(self, builder, valtype): optval = self.make_helper(builder, types.Optional(valtype)) optval.valid = cgutils.false_bit return optval._getvalue()
def test_optional(self): ty = types.Optional(types.int32) self.check_pickling(ty)
def test_cleanup_optional(self): mem = memoryview(bytearray(b"xyz")) tp = types.Optional(types.Buffer(types.intc, 1, "C")) self.check_argument_cleanup(tp, mem)