2
2
3
3
# macro wrappers for various reflection functions
4
4
5
- using Base: typesof, insert!, replace_ref_begin_end!,
6
- infer_return_type, infer_exception_type, infer_effects, code_ircode
5
+ using Base: insert!, replace_ref_begin_end!,
6
+ infer_return_type, infer_exception_type, infer_effects, code_ircode, isexpr
7
7
8
8
# defined in Base so it's possible to time all imports, including InteractiveUtils and its deps
9
9
# via. `Base.@time_imports` etc.
10
10
import Base: @time_imports , @trace_compile , @trace_dispatch
11
11
12
- separate_kwargs (args... ; kwargs... ) = (args, values (kwargs))
12
+ typesof_expr (args:: Vector{Any} , where_params:: Union{Nothing, Vector{Any}} = nothing ) = rewrap_where (:(Tuple{$ (get_typeof .(args)... )}), where_params)
13
+
14
+ function extract_where_parameters (ex:: Expr )
15
+ isexpr (ex, :where ) || return ex, nothing
16
+ ex. args[1 ], ex. args[2 : end ]
17
+ end
18
+
19
+ function rewrap_where (ex:: Expr , where_params:: Union{Nothing, Vector{Any}} )
20
+ isnothing (where_params) && return ex
21
+ Expr (:where , ex, esc .(where_params)... )
22
+ end
23
+
24
+ function get_typeof (@nospecialize ex)
25
+ isexpr (ex, :(:: ), 1 ) && return esc (ex. args[1 ])
26
+ if isexpr (ex, :... , 1 )
27
+ splatted = ex. args[1 ]
28
+ isexpr (splatted, :(:: ), 1 ) && return Expr (:curly , :Vararg , esc (splatted. args[1 ]))
29
+ return :(Any[Core. Typeof (x) for x in $ (esc (splatted))]. .. )
30
+ end
31
+ return :(Core. Typeof ($ (esc (ex))))
32
+ end
13
33
14
34
"""
15
35
Transform a dot expression into one where each argument has been replaced by a
@@ -20,7 +40,7 @@ function recursive_dotcalls!(ex, args, i=1)
20
40
if ! (ex isa Expr) || ((ex. head != = :. || ! (ex. args[2 ] isa Expr)) &&
21
41
(ex. head != = :call || string (ex. args[1 ])[1 ] != ' .' ))
22
42
newarg = Symbol (' x' , i)
23
- if Meta . isexpr (ex, :... )
43
+ if isexpr (ex, :... )
24
44
push! (args, only (ex. args))
25
45
return Expr (:... , newarg), i+ 1
26
46
else
@@ -37,20 +57,121 @@ function recursive_dotcalls!(ex, args, i=1)
37
57
return ex, i
38
58
end
39
59
60
+ function extract_farg (@nospecialize arg)
61
+ ! isexpr (arg, :(:: ), 1 ) && return esc (arg)
62
+ fT = esc (arg. args[1 ])
63
+ :($ construct_callable ($ fT))
64
+ end
65
+
66
+ function construct_callable (@nospecialize (func:: Type ))
67
+ # Support function singleton types such as `(::typeof(f))(args...)`
68
+ Base. issingletontype (func) && isdefined (func, :instance ) && return func. instance
69
+ # Don't support type annotations otherwise, we don't want to give wrong answers
70
+ # for callables such as `(::Returns{Int})(args...)` where using `Returns{Int}`
71
+ # would give us code for the constructor, not for the callable object.
72
+ throw (ArgumentError (" If a function type is explicitly provided, it must be a singleton whose only instance is the callable object" ))
73
+ end
74
+
75
+ function separate_kwargs (exs:: Vector{Any} )
76
+ args = []
77
+ kwargs = []
78
+ for ex in exs
79
+ if isexpr (ex, :kw )
80
+ push! (kwargs, ex)
81
+ elseif isexpr (ex, :parameters )
82
+ for kw in ex. args
83
+ push! (kwargs, kw)
84
+ end
85
+ else
86
+ push! (args, ex)
87
+ end
88
+ end
89
+ args, kwargs
90
+ end
91
+
92
+ function are_kwargs_valid (kwargs:: Vector{Any} )
93
+ for kwarg in kwargs
94
+ isexpr (kwarg, :... , 1 ) && continue
95
+ isexpr (kwarg, :kw , 2 ) && isa (kwarg. args[1 ], Symbol) && continue
96
+ isa (kwarg, Symbol) && continue
97
+ return false
98
+ end
99
+ return true
100
+ end
101
+
102
+ # Generate an expression that merges `kwargs` onto a single `NamedTuple`
103
+ function generate_merged_namedtuple_type (kwargs:: Vector{Any} )
104
+ nts = Any[]
105
+ ntargs = Pair{Symbol, Any}[]
106
+ for ex in kwargs
107
+ if isexpr (ex, :... , 1 )
108
+ if ! isempty (ntargs)
109
+ # Construct a `NamedTuple` containing the previous parameters.
110
+ push! (nts, generate_namedtuple_type (ntargs))
111
+ empty! (ntargs)
112
+ end
113
+ push! (nts, Expr (:call , typeof_nt, esc (ex. args[1 ])))
114
+ elseif isexpr (ex, :kw , 2 )
115
+ push! (ntargs, ex. args[1 ]:: Symbol => get_typeof (ex. args[2 ]))
116
+ else
117
+ ex:: Symbol
118
+ push! (ntargs, ex => get_typeof (ex))
119
+ end
120
+ end
121
+ ! isempty (ntargs) && push! (nts, generate_namedtuple_type (ntargs))
122
+ return :($ merge_namedtuple_types ($ (nts... )))
123
+ end
124
+
125
+ function generate_namedtuple_type (ntargs:: Vector{Pair{Symbol, Any}} )
126
+ names = Expr (:tuple )
127
+ tt = Expr (:curly , :Tuple )
128
+ for (name, type) in ntargs
129
+ push! (names. args, QuoteNode (name))
130
+ push! (tt. args, type)
131
+ end
132
+ return :(NamedTuple{$ names, $ tt})
133
+ end
134
+
135
+ typeof_nt (nt:: NamedTuple ) = typeof (nt)
136
+ typeof_nt (nt:: Base.Pairs ) = typeof (values (nt))
137
+
138
+ function merge_namedtuple_types (nt:: Type{<:NamedTuple} , nts:: Type{<:NamedTuple} ...)
139
+ @nospecialize
140
+ isempty (nts) && return nt
141
+ names = Symbol[]
142
+ types = Any[]
143
+ for nt in (nt, nts... )
144
+ for (name, type) in zip (fieldnames (nt), fieldtypes (nt))
145
+ i = findfirst (== (name), names)
146
+ if isnothing (i)
147
+ push! (names, name)
148
+ push! (types, type)
149
+ else
150
+ types[i] = type
151
+ end
152
+ end
153
+ end
154
+ NamedTuple{Tuple (names), Tuple{types... }}
155
+ end
156
+
40
157
function gen_call_with_extracted_types (__module__, fcn, ex0, kws= Expr[])
41
- if Meta . isexpr (ex0, :ref )
158
+ if isexpr (ex0, :ref )
42
159
ex0 = replace_ref_begin_end! (ex0)
43
160
end
44
161
# assignments get bypassed: @edit a = f(x) <=> @edit f(x)
45
162
if isa (ex0, Expr) && ex0. head == :(= ) && isa (ex0. args[1 ], Symbol) && isempty (kws)
46
163
return gen_call_with_extracted_types (__module__, fcn, ex0. args[2 ])
47
164
end
165
+ where_params = nothing
166
+ if isa (ex0, Expr)
167
+ ex0, where_params = extract_where_parameters (ex0)
168
+ end
48
169
if isa (ex0, Expr)
49
- if ex0. head === :do && Meta . isexpr (get (ex0. args, 1 , nothing ), :call )
170
+ if ex0. head === :do && isexpr (get (ex0. args, 1 , nothing ), :call )
50
171
if length (ex0. args) != 2
51
172
return Expr (:call , :error , " ill-formed do call" )
52
173
end
53
- i = findlast (a -> (Meta . isexpr (a, :kw ) || Meta . isexpr (a, :parameters )), ex0. args[1 ]. args)
174
+ i = findlast (@nospecialize (a) -> (isexpr (a, :kw ) || isexpr (a, :parameters )), ex0. args[1 ]. args)
54
175
args = copy (ex0. args[1 ]. args)
55
176
insert! (args, (isnothing (i) ? 2 : 1 + i:: Int ), ex0. args[2 ])
56
177
ex0 = Expr (:call , args... )
@@ -66,8 +187,7 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
66
187
dotfuncdef = Expr (:local , Expr (:(= ), Expr (:call , dotfuncname, xargs... ), ex))
67
188
return quote
68
189
$ (esc (dotfuncdef))
69
- local args = $ typesof ($ (map (esc, args)... ))
70
- $ (fcn)($ (esc (dotfuncname)), args; $ (kws... ))
190
+ $ (fcn)($ (esc (dotfuncname)), $ (typesof_expr (args, where_params)); $ (kws... ))
71
191
end
72
192
elseif ! codemacro
73
193
fully_qualified_symbol = true # of the form A.B.C.D
@@ -80,7 +200,9 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
80
200
ex1 = ex1. args[1 ]
81
201
end
82
202
fully_qualified_symbol &= ex1 isa Symbol
83
- if fully_qualified_symbol
203
+ if fully_qualified_symbol || isexpr (ex1, :(:: ), 1 )
204
+ getproperty_ex = :($ (fcn)(Base. getproperty, $ (typesof_expr (ex0. args, where_params))))
205
+ isexpr (ex0. args[1 ], :(:: ), 1 ) && return getproperty_ex
84
206
return quote
85
207
local arg1 = $ (esc (ex0. args[1 ]))
86
208
if isa (arg1, Module)
@@ -90,8 +212,7 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
90
212
:(error (" expression is not a function call" ))
91
213
end )
92
214
else
93
- local args = $ typesof ($ (map (esc, ex0. args)... ))
94
- $ (fcn)(Base. getproperty, args)
215
+ $ getproperty_ex
95
216
end
96
217
end
97
218
else
@@ -102,32 +223,35 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
102
223
end
103
224
end
104
225
end
105
- if any (a-> (Meta. isexpr (a, :kw ) || Meta. isexpr (a, :parameters )), ex0. args)
106
- return quote
107
- local arg1 = $ (esc (ex0. args[1 ]))
108
- local args, kwargs = $ separate_kwargs ($ (map (esc, ex0. args[2 : end ])... ))
109
- $ (fcn)(Core. kwcall,
110
- Tuple{typeof (kwargs), Core. Typeof (arg1), map (Core. Typeof, args)... };
111
- $ (kws... ))
226
+ if any (@nospecialize (a)-> (isexpr (a, :kw ) || isexpr (a, :parameters )), ex0. args)
227
+ args, kwargs = separate_kwargs (ex0. args)
228
+ are_kwargs_valid (kwargs) || return quote
229
+ error (" keyword argument format unrecognized; they must be of the form `x` or `x = <value>`" )
230
+ $ (esc (ex0)) # trigger syntax errors if any
112
231
end
232
+ nt = generate_merged_namedtuple_type (kwargs)
233
+ tt = rewrap_where (:(Tuple{$ nt, $ (get_typeof .(args)... )}), where_params)
234
+ return :($ (fcn)(Core. kwcall, $ tt; $ (kws... )))
113
235
elseif ex0. head === :call
236
+ argtypes = Any[get_typeof (arg) for arg in ex0. args[2 : end ]]
114
237
if ex0. args[1 ] === :^ && length (ex0. args) >= 3 && isa (ex0. args[3 ], Int)
115
- return Expr (:call , fcn, :(Base. literal_pow),
116
- Expr (:call , typesof, esc (ex0. args[1 ]), esc (ex0. args[2 ]),
117
- esc (Val (ex0. args[3 ]))))
238
+ farg = :(Base. literal_pow)
239
+ pushfirst! (argtypes, :(typeof (^ )))
240
+ argtypes[3 ] = :(Val{$ (ex0. args[3 ])})
241
+ else
242
+ farg = extract_farg (ex0. args[1 ])
118
243
end
119
- return Expr (:call , fcn, esc (ex0. args[1 ]),
120
- Expr (:call , typesof, map (esc, ex0. args[2 : end ])... ),
121
- kws... )
244
+ tt = rewrap_where (:(Tuple{$ (argtypes... )}), where_params)
245
+ return Expr (:call , fcn, farg, tt, kws... )
122
246
elseif ex0. head === :(= ) && length (ex0. args) == 2
123
247
lhs, rhs = ex0. args
124
248
if isa (lhs, Expr)
125
249
if lhs. head === :(.)
126
250
return Expr (:call , fcn, Base. setproperty!,
127
- Expr ( :call , typesof, map (esc, lhs. args) ... , esc ( rhs) ), kws... )
251
+ typesof_expr (Any[ lhs. args... , rhs], where_params ), kws... )
128
252
elseif lhs. head === :ref
129
253
return Expr (:call , fcn, Base. setindex!,
130
- Expr ( :call , typesof, esc ( lhs. args[1 ]), esc ( rhs), map (esc, lhs. args[2 : end ]) ... ), kws... )
254
+ typesof_expr (Any[ lhs. args[1 ], rhs, lhs. args[2 : end ]. .. ], where_params ), kws... )
131
255
end
132
256
end
133
257
elseif ex0. head === :vcat || ex0. head === :typed_vcat
@@ -138,23 +262,19 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
138
262
f, hf = Base. typed_vcat, Base. typed_hvcat
139
263
args = ex0. args[2 : end ]
140
264
end
141
- if any (a -> isa (a,Expr) && a. head === :row , args)
265
+ if any (@nospecialize (a) -> isa (a,Expr) && a. head === :row , args)
142
266
rows = Any[ (isa (x,Expr) && x. head === :row ? x. args : Any[x]) for x in args ]
143
267
lens = map (length, rows)
144
- return Expr (:call , fcn, hf,
145
- Expr (:call , typesof,
146
- (ex0. head === :vcat ? [] : Any[esc (ex0. args[1 ])]). .. ,
147
- Expr (:tuple , lens... ),
148
- map (esc, vcat (rows... ))... ), kws... )
268
+ args = Any[Expr (:tuple , lens... ); vcat (rows... )]
269
+ ex0. head === :typed_vcat && pushfirst! (args, ex0. args[1 ])
270
+ return Expr (:call , fcn, hf, typesof_expr (args, where_params), kws... )
149
271
else
150
- return Expr (:call , fcn, f,
151
- Expr (:call , typesof, map (esc, ex0. args)... ), kws... )
272
+ return Expr (:call , fcn, f, typesof_expr (ex0. args, where_params), kws... )
152
273
end
153
274
else
154
275
for (head, f) in (:ref => Base. getindex, :hcat => Base. hcat, :(.) => Base. getproperty, :vect => Base. vect, Symbol (" '" ) => Base. adjoint, :typed_hcat => Base. typed_hcat, :string => string)
155
276
if ex0. head === head
156
- return Expr (:call , fcn, f,
157
- Expr (:call , typesof, map (esc, ex0. args)... ), kws... )
277
+ return Expr (:call , fcn, f, typesof_expr (ex0. args, where_params), kws... )
158
278
end
159
279
end
160
280
end
@@ -170,16 +290,14 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
170
290
171
291
exret = Expr (:none )
172
292
if ex. head === :call
173
- if any (e -> ( isa (e, Expr) && e . head === :( ... ) ), ex0. args) &&
293
+ if any (@nospecialize (x) -> isexpr (x, : ... ), ex0. args) &&
174
294
(ex. args[1 ] === GlobalRef (Core,:_apply_iterate ) ||
175
295
ex. args[1 ] === GlobalRef (Base,:_apply_iterate ))
176
296
# check for splatting
177
297
exret = Expr (:call , ex. args[2 ], fcn,
178
- Expr (:tuple , esc (ex. args[3 ]),
179
- Expr (:call , typesof, map (esc, ex. args[4 : end ])... )))
298
+ Expr (:tuple , extract_farg (ex. args[3 ]), typesof_expr (ex. args[4 : end ], where_params)))
180
299
else
181
- exret = Expr (:call , fcn, esc (ex. args[1 ]),
182
- Expr (:call , typesof, map (esc, ex. args[2 : end ])... ), kws... )
300
+ exret = Expr (:call , fcn, extract_farg (ex. args[1 ]), typesof_expr (ex. args[2 : end ], where_params), kws... )
183
301
end
184
302
end
185
303
if ex. head === :thunk || exret. head === :none
@@ -460,7 +578,7 @@ For `@activate Compiler`, the following options are available:
460
578
"""
461
579
macro activate (what)
462
580
options = Symbol[]
463
- if Meta . isexpr (what, :ref )
581
+ if isexpr (what, :ref )
464
582
Component = what. args[1 ]
465
583
for i = 2 : length (what. args)
466
584
arg = what. args[i]
0 commit comments