optional.py 4.08 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import operator

from numba.core import types, typing, cgutils

from numba.core.imputils import (lower_cast, lower_builtin,
                                 lower_getattr_generic, impl_ret_untracked,
                                 lower_setattr_generic)


def always_return_true_impl(context, builder, sig, args):
    return cgutils.true_bit


def always_return_false_impl(context, builder, sig, args):
    return cgutils.false_bit


def optional_is_none(context, builder, sig, args):
    """
    Check if an Optional value is invalid
    """
    [lty, rty] = sig.args
    [lval, rval] = args

    # Make sure None is on the right
    if lty == types.none:
        lty, rty = rty, lty
        lval, rval = rval, lval

    opt_type = lty
    opt_val = lval

    opt = context.make_helper(builder, opt_type, opt_val)
    res = builder.not_(cgutils.as_bool_bit(builder, opt.valid))
    return impl_ret_untracked(context, builder, sig.return_type, res)


# None is/not None
lower_builtin(operator.is_, types.none, types.none)(always_return_true_impl)

# Optional is None
lower_builtin(operator.is_, types.Optional, types.none)(optional_is_none)
lower_builtin(operator.is_, types.none, types.Optional)(optional_is_none)


@lower_getattr_generic(types.Optional)
def optional_getattr(context, builder, typ, value, attr):
    """
    Optional.__getattr__ => redirect to the wrapped type.
    """
    inner_type = typ.type
    val = context.cast(builder, value, typ, inner_type)
    imp = context.get_getattr(inner_type, attr)
    return imp(context, builder, inner_type, val, attr)


@lower_setattr_generic(types.Optional)
def optional_setattr(context, builder, sig, args, attr):
    """
    Optional.__setattr__ => redirect to the wrapped type.
    """
    basety, valty = sig.args
    target, val = args
    target_type = basety.type
    target = context.cast(builder, target, basety, target_type)

    newsig = typing.signature(sig.return_type, target_type, valty)
    imp = context.get_setattr(attr, newsig)
    return imp(builder, (target, val))


@lower_cast(types.Optional, types.Optional)
def optional_to_optional(context, builder, fromty, toty, val):
    """
    The handling of optional->optional cast must be special cased for
    correct propagation of None value.  Given type T and U. casting of
    T? to U? (? denotes optional) should always succeed.   If the from-value
    is None, the None value the casted value (U?) should be None; otherwise,
    the from-value is casted to U. This is different from casting T? to U,
    which requires the from-value must not be None.
    """
    optval = context.make_helper(builder, fromty, value=val)
    validbit = cgutils.as_bool_bit(builder, optval.valid)
    # Create uninitialized optional value
    outoptval = context.make_helper(builder, toty)

    with builder.if_else(validbit) as (is_valid, is_not_valid):
        with is_valid:
            # Cast internal value
            outoptval.valid = cgutils.true_bit
            outoptval.data = context.cast(builder, optval.data,
                                          fromty.type, toty.type)

        with is_not_valid:
            # Store None to result
            outoptval.valid = cgutils.false_bit
            outoptval.data = cgutils.get_null_value(
                outoptval.data.type)

    return outoptval._getvalue()


@lower_cast(types.Any, types.Optional)
def any_to_optional(context, builder, fromty, toty, val):
    if fromty == types.none:
        return context.make_optional_none(builder, toty.type)
    else:
        val = context.cast(builder, val, fromty, toty.type)
        return context.make_optional_value(builder, toty.type, val)


@lower_cast(types.Optional, types.Any)
@lower_cast(types.Optional, types.Boolean)
def optional_to_any(context, builder, fromty, toty, val):
    optval = context.make_helper(builder, fromty, value=val)
    validbit = cgutils.as_bool_bit(builder, optval.valid)
    with builder.if_then(builder.not_(validbit), likely=False):
        msg = "expected %s, got None" % (fromty.type,)
        context.call_conv.return_user_exc(builder, TypeError, (msg,))

    return context.cast(builder, optval.data, fromty.type, toty)