31
31
using Base. Broadcast: ziptype
32
32
end
33
33
34
- @inline function broadcast_lift (f , x)
35
- if null_safe_op (f, eltype (x ))
36
- return @compat Nullable (f (x . value) , ! isnull (x) )
34
+ @inline @generated function broadcast_lift {F, T} (f :: F , x:: NTuple{1, T} )
35
+ if null_safe_op (f. instance , eltype (T ))
36
+ return :( Nullable (f (unsafe_get (x[ 1 ])) , ! isnull (x[ 1 ])) )
37
37
else
38
- U = Core. Inference. return_type (f, Tuple{eltype (x)})
39
- if isnull (x)
40
- return Nullable {U} ()
41
- else
42
- return Nullable (f (unsafe_get (x)))
38
+ U = Core. Inference. return_type (f. instance, Tuple{eltype (T)})
39
+ return quote
40
+ if isnull (x)
41
+ return Nullable {$U} ()
42
+ else
43
+ return Nullable (f (unsafe_get (x[1 ])))
44
+ end
43
45
end
44
46
end
45
47
end
46
48
47
- @inline function broadcast_lift (f, x1, x2)
48
- if null_safe_op (f, eltype (x1), eltype (x2))
49
- return @compat Nullable (f (x1. value, x2. value), ! (isnull (x1) | isnull (x2)))
49
+ @inline @generated function broadcast_lift {F, S, T} (f:: F , x:: Tuple{S, T} )
50
+ if null_safe_op (f. instance, eltype (S), eltype (T))
51
+ return :( Nullable (f (unsafe_get (x[1 ]), unsafe_get (x[2 ])),
52
+ ! (isnull (x[1 ]) | isnull (x[2 ]))) )
50
53
else
51
- U = Core. Inference. return_type (f, Tuple{eltype (x1), eltype (x2)})
52
- if isnull (x1) | isnull (x2)
53
- return Nullable {U} ()
54
- else
55
- return Nullable (f (unsafe_get (x1), unsafe_get (x2)))
54
+ U = Core. Inference. return_type (f. instance, Tuple{eltype (S), eltype (T)})
55
+ return quote
56
+ if isnull (x[1 ]) | isnull (x[2 ])
57
+ return Nullable {$U} ()
58
+ else
59
+ return Nullable (f (unsafe_get (x[1 ]), unsafe_get (x[2 ])))
60
+ end
56
61
end
57
62
end
58
63
end
@@ -62,29 +67,34 @@ eltypes(x) = Tuple{eltype(x)}
62
67
eltypes (x, xs... ) = Tuple{eltype (x), eltypes (xs... ). parameters... }
63
68
64
69
"""
65
- broadcast_lift(f, xs... )
70
+ broadcast_lift(f, x )
66
71
67
- Lift function `f`, passing it arguments `xs... `, using standard lifting semantics:
68
- for a function call `f(xs ...)`, return null if any `x` in `xs ` is null; otherwise,
69
- return `f` applied to values of `xs `.
72
+ Lift function `f`, passing it arguments from the tuple `x `, using standard lifting semantics:
73
+ for a function call `f(x ...)`, return null if any `x` in `x ` is null; otherwise,
74
+ return `f` applied to values of `x `.
70
75
"""
71
- @inline function broadcast_lift (f, xs ... )
72
- if null_safe_op (f, eltypes (xs ). parameters... )
76
+ @inline @generated function broadcast_lift {F} (f :: F , x :: Tuple )
77
+ if null_safe_op (f. instance , eltypes (x ). parameters... )
73
78
# TODO : find a more efficient approach than mapreduce
74
79
# (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
75
- return @compat Nullable (f (unsafe_get .(xs) ... ), ! mapreduce (isnull, | , xs) )
80
+ return :( Nullable (f (unsafe_get .(x) ), ! mapreduce (isnull, | , x)) )
76
81
else
77
- U = Core. Inference. return_type (f, eltypes (xs... ))
78
- # TODO : find a more efficient approach than mapreduce
79
- # (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
80
- if mapreduce (isnull, | , xs)
81
- return Nullable {U} ()
82
- else
83
- return Nullable (f (map (unsafe_get, xs)... ))
82
+ U = Core. Inference. return_type (f. instance, eltypes (x))
83
+ return quote
84
+ # TODO : find a more efficient approach than mapreduce
85
+ # (i.e. one which gets lowered to just isnull(x1) | isnull(x2) | ...)
86
+ if mapreduce (isnull, | , x)
87
+ return Nullable {$U} ()
88
+ else
89
+ return Nullable (f (map (unsafe_get, x)... ))
90
+ end
84
91
end
85
92
end
86
93
end
87
94
95
+ call_broadcast {F, N} (f:: F , dest, As:: Vararg{NullableArray, N} ) =
96
+ invoke (broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f, dest, As... )
97
+
88
98
"""
89
99
broadcast(f, As::NullableArray...)
90
100
@@ -97,15 +107,19 @@ Note that this method's signature specifies the source `As` arrays as all
97
107
of both `Array`s and `NullableArray`s will fall back to the standard implementation
98
108
of `broadcast` (i.e. without lifting).
99
109
"""
100
- function Base. broadcast {N} (f, As:: Vararg{NullableArray, N} )
101
- f2 (x... ) = broadcast_lift (f, x... )
110
+ function Base. broadcast {F, N} (f:: F , As:: Vararg{NullableArray, N} )
111
+ # These definitions are needed to avoid allocation due to splatting
112
+ f2 (x1) = broadcast_lift (f, (x1,))
113
+ f2 (x1, x2) = broadcast_lift (f, (x1, x2))
114
+ f2 (x... ) = broadcast_lift (f, x)
115
+
102
116
T = _default_eltype (Base. Generator{ziptype (As... ), ftype (f2, As... )})
103
117
if isleaftype (T) && ! (T <: Nullable )
104
118
dest = similar (Array{eltype (T)}, broadcast_indices (As... ))
105
119
else
106
120
dest = similar (NullableArray{eltype (T)}, broadcast_indices (As... ))
107
121
end
108
- invoke (broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f2, dest, As... )
122
+ call_broadcast ( f2, dest, As... )
109
123
end
110
124
111
125
"""
@@ -120,15 +134,18 @@ source `As` arrays as all `NullableArray`s. Thus, calling `broadcast!` on a argu
120
134
consisting of both `Array`s and `NullableArray`s will fall back to the standard implementation
121
135
of `broadcast!` (i.e. without lifting).
122
136
"""
123
- function Base. broadcast! {N} (f, dest:: NullableArray , As:: Vararg{NullableArray, N} )
124
- f2 (x... ) = broadcast_lift (f, x... )
125
- invoke (broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, N}}, f2, dest, As... )
137
+ function Base. broadcast! {F, N} (f:: F , dest:: NullableArray , As:: Vararg{NullableArray, N} )
138
+ # These definitions are needed to avoid allocation due to splatting
139
+ f2 (x1) = broadcast_lift (f, (x1,))
140
+ f2 (x1, x2) = broadcast_lift (f, (x1, x2))
141
+ f2 (x... ) = broadcast_lift (f, x)
142
+ call_broadcast (f2, dest, As... )
126
143
end
127
144
128
145
# To fix ambiguity
129
- function Base. broadcast! (f , dest:: NullableArray )
130
- f2 (x ... ) = broadcast_lift (f, x ... )
131
- invoke (broadcast!, Tuple{Function, AbstractArray, Vararg{AbstractArray, 0 }}, f2, dest)
146
+ function Base. broadcast! {F} (f :: F , dest:: NullableArray )
147
+ f2 () = broadcast_lift (f, () )
148
+ call_broadcast ( f2, dest)
132
149
end
133
150
134
151
# broadcasted ops
0 commit comments