Skip to content
← Back

src/syntax/process/compile-expressions/pack_wrap_builder.ghul

1
namespace Syntax.Process is
2
use Semantic.Types.Type
3
4
use IR.Values.Value
5
6
// Presents a function value of one argument pack shape in the other.
7
// A function of two or more parameters going into a formal that takes
8
// the pack as one tuple is wrapped in a thunk that takes the tuple,
9
// unpacks it and calls the function; the other direction packs the
10
// elements into the tuple the function takes.
11
//
12
// The function value is evaluated once, where it stands, into a local
13
// the thunk captures, so an operand with effects of its own - a call
14
// that builds state - runs once however often the thunk is called.
15
//
16
// Nothing here touches a syntax tree. The thunk is a closure symbol
17
// with no literal behind it, and its body is emitted from the
18
// description recorded on it. A call is compiled once for every walk
19
// of the body it stands in, so the thunk made for a given argument of
20
// a given call is kept and reused rather than declared again.
21
class PACK_WRAP_BUILDER is
22
_symbol_table: Semantic.SYMBOL_TABLE
23
_symbol_loader: Semantic.SYMBOL_LOADER
24
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
25
26
_thunks: Collections.MAP[Trees.Node, Collections.MAP[int, THUNK_PARTS]]
27
28
_index: int
29
30
init(
31
symbol_table: Semantic.SYMBOL_TABLE,
32
symbol_loader: Semantic.SYMBOL_LOADER,
33
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
34
) is
35
_symbol_table = symbol_table
36
_symbol_loader = symbol_loader
37
_innate_symbol_lookup = innate_symbol_lookup
38
39
_thunks = Collections.MAP[Trees.Node, Collections.MAP[int, THUNK_PARTS]]()
40
si
41
42
// The type `function_type` has once its parameters after the
43
// first `fixed_count` are taken as one tuple. Absent where the
44
// type is not one this applies to, or where a parameter is not
45
// settled enough to be a tuple element.
46
packed_type(function_type: Type?, fixed_count: int) -> Type? =>
47
packed_type(function_type, fixed_count, false)
48
49
// As above. `provisional` lets a parameter still being inferred
50
// stand in the tuple, for a value that nothing has typed yet and
51
// that a later use of the call's result will.
52
packed_type(function_type: Type?, fixed_count: int, provisional: bool) -> Type? is
53
if !function_type? \/ !function_type.is_function then
54
return null
55
fi
56
57
let count = Semantic.ARGUMENT_PACK.parameter_count(function_type)
58
let arity = count - fixed_count
59
60
if arity < 2 \/ arity > Semantic.ARGUMENT_PACK.MAXIMUM_ARITY then
61
return null
62
fi
63
64
let parameters = Collections.LIST[Type]()
65
let elements = Collections.LIST[Type]()
66
67
for i in 0..count do
68
let parameter = function_type.arguments[i]
69
70
if parameter.is_error \/ (parameter.contains_inferred /\ !provisional) \/ parameter.is_wild then
71
return null
72
fi
73
74
if i < fixed_count then
75
parameters.add(parameter)
76
else
77
elements.add(parameter)
78
fi
79
od
80
81
parameters.add(_innate_symbol_lookup.get_tuple_type(elements, null))
82
83
parameters.add(
84
if function_type.is_action then
85
_innate_symbol_lookup.get_void_type()
86
else
87
function_type.arguments[count]
88
fi
89
)
90
91
return _innate_symbol_lookup.get_function_type(parameters, function_type.is_pure_function)
92
si
93
94
95
// The type `function_type` has once the tuple it takes after its
96
// first `fixed_count` parameters is spread out as parameters of
97
// its own. Absent where the type is not one this applies to.
98
spread_type(function_type: Type?, fixed_count: int) -> Type? is
99
if !function_type? \/ !function_type.is_function then
100
return null
101
fi
102
103
let count = Semantic.ARGUMENT_PACK.parameter_count(function_type)
104
105
if count != fixed_count + 1 then
106
return null
107
fi
108
109
let tuple = function_type.arguments[fixed_count]
110
111
if
112
!(tuple.is_value_tuple \/ isa Semantic.Types.TUPLE(tuple)) \/
113
tuple.arguments.count < 2 \/
114
tuple.arguments.count > Semantic.ARGUMENT_PACK.MAXIMUM_ARITY \/
115
tuple.contains_inferred
116
then
117
return null
118
fi
119
120
let parameters = Collections.LIST[Type]()
121
122
for i in 0..fixed_count do
123
parameters.add(function_type.arguments[i])
124
od
125
126
for element in tuple.arguments do
127
parameters.add(element)
128
od
129
130
parameters.add(
131
if function_type.is_action then
132
_innate_symbol_lookup.get_void_type()
133
else
134
function_type.arguments[count]
135
fi
136
)
137
138
return _innate_symbol_lookup.get_function_type(parameters, function_type.is_pure_function)
139
si
140
141
// Wraps `value`, a function of the pack's elements, as a function
142
// of their tuple. `key` and `index` say which argument of which
143
// call this is.
144
unpack(
145
key: Trees.Node,
146
index: int,
147
location: Source.LOCATION,
148
value: Value,
149
fixed_count: int
150
) -> Value? is
151
let callee_type = value.type
152
153
let presented = packed_type(callee_type, fixed_count, true)
154
155
if !callee_type? \/ !presented? then
156
return null
157
fi
158
159
let elements = Collections.LIST[Type]()
160
161
for i in fixed_count..Semantic.ARGUMENT_PACK.parameter_count(callee_type) do
162
elements.add(callee_type.arguments[i])
163
od
164
165
return _wrap(key, index, location, value, callee_type, presented, true, fixed_count, elements)
166
si
167
168
// Wraps `value`, a function of the pack's tuple, as a function of
169
// its elements. `callee_type` is the type it is called at, which
170
// may be more settled than the value's own.
171
pack(
172
key: Trees.Node,
173
index: int,
174
location: Source.LOCATION,
175
value: Value,
176
callee_type: Type,
177
fixed_count: int
178
) -> Value? is
179
let presented = spread_type(callee_type, fixed_count)
180
181
if !presented? then
182
return null
183
fi
184
185
let elements = Collections.LIST[Type]()
186
187
for element in callee_type.arguments[fixed_count].arguments do
188
elements.add(element)
189
od
190
191
return _wrap(key, index, location, value, callee_type, presented, false, fixed_count, elements)
192
si
193
194
_wrap(
195
key: Trees.Node,
196
index: int,
197
location: Source.LOCATION,
198
value: Value,
199
callee_type: Type,
200
presented: Type,
201
is_unpack: bool,
202
fixed_count: int,
203
elements: Collections.List[Type]
204
) -> Value? is
205
let enclosing = _symbol_table.current_function
206
207
if !enclosing? then
208
return null
209
fi
210
211
let parts = _parts_for(key, index, location, enclosing)
212
213
if !parts? then
214
return null
215
fi
216
217
let thunk = parts.thunk
218
219
let parameter_types = Collections.LIST[Type]()
220
let parameter_names = Collections.LIST[string]()
221
222
for i in 0..Semantic.ARGUMENT_PACK.parameter_count(presented) do
223
parameter_types.add(presented.arguments[i])
224
parameter_names.add("$pack_{i}")
225
226
_parameter(thunk, location, i).set_type(presented.arguments[i])
227
od
228
229
thunk.arguments = parameter_types
230
thunk.argument_names = parameter_names
231
232
thunk.set_return_type(
233
if presented.is_action then
234
_innate_symbol_lookup.get_void_type()
235
else
236
presented.arguments[presented.arguments.count - 1]
237
fi
238
)
239
240
for t in parameter_types do
241
_record_type_arguments(thunk, t)
242
od
243
244
_record_type_arguments(thunk, thunk.return_type!)
245
246
let block = IR.Values.PACK_WRAP(presented, value)
247
248
// A function named where it stands, needing no receiver and no
249
// type arguments, is called directly: the thunk captures
250
// nothing, so its delegate is built once and kept.
251
if let target = _direct_target(value) then
252
thunk.pack_thunk = Semantic.Symbols.PACK_THUNK(is_unpack, fixed_count, elements, null, callee_type, target)
253
254
block.add(thunk.load(location, _symbol_loader))
255
256
return block
257
fi
258
259
parts.hoist.set_type(callee_type)
260
261
thunk.pack_thunk =
262
Semantic.Symbols.PACK_THUNK(
263
is_unpack,
264
fixed_count,
265
elements,
266
thunk.load_captured_value(location, parts.hoist, _symbol_loader),
267
callee_type,
268
null
269
)
270
271
block.add(IR.Values.DECLARE_LOCAL(parts.hoist.il_name, callee_type))
272
block.add(IR.Values.Store.LOCAL_VARIABLE(parts.hoist, value))
273
block.add(thunk.load(location, _symbol_loader))
274
275
return block
276
si
277
278
_direct_target(value: Value) -> Semantic.Symbols.Function? is
279
let function = value.referenced_function
280
281
if
282
!function? \/
283
function.is_instance \/
284
function.is_generic \/
285
isa Semantic.Symbols.Closure(function) \/
286
!(isa Semantic.Symbols.GLOBAL_FUNCTION(function) \/ isa Semantic.Symbols.STATIC_METHOD(function))
287
then
288
return null
289
fi
290
291
return function
292
si
293
294
_parameter(
295
thunk: Semantic.Symbols.Closure,
296
location: Source.LOCATION,
297
index: int
298
) -> Semantic.Symbols.Variable is
299
let name = "$pack_{index}"
300
301
if let existing: Semantic.Symbols.Variable = thunk.find_direct(name) then
302
return existing
303
fi
304
305
thunk.start_declaring_arguments()
306
307
let declared = cast Semantic.Symbols.Variable?(thunk.declare_variable(location, name, false, null))!
308
309
thunk.end_declaring_arguments()
310
311
declared.mark_synthesized()
312
declared.define()
313
314
return declared
315
si
316
317
_record_type_arguments(thunk: Semantic.Symbols.Closure, type: Type) is
318
let collected = Collections.LIST[Semantic.Symbols.Symbol]()
319
320
Semantic.INFERENCE_HELPERS.collect_method_level_type_variables(type, collected)
321
322
for u in collected do
323
thunk.add_type_argument_reference(u)
324
od
325
si
326
327
_parts_for(
328
key: Trees.Node,
329
index: int,
330
location: Source.LOCATION,
331
enclosing: Semantic.Symbols.Function
332
) -> THUNK_PARTS? is
333
let for_key: Collections.MAP[int, THUNK_PARTS] mut
334
335
if !_thunks.try_get_value(key, for_key ref) then
336
for_key = Collections.MAP[int, THUNK_PARTS]()
337
_thunks[key] = for_key
338
fi
339
340
let existing: THUNK_PARTS mut
341
342
if for_key.try_get_value(index, existing ref) then
343
return existing
344
fi
345
346
let scope = Semantic.BLOCK_SCOPE(_symbol_table.current_scope)
347
348
let local_id_generator = IoC.CONTAINER.instance.local_id_generator
349
350
local_id_generator.enter_function()
351
352
let hoist = cast Semantic.Symbols.Variable?(scope.declare_variable(location, "$pack_callee_{_index}", false, null))
353
354
local_id_generator.leave_function()
355
356
let thunk =
357
cast Semantic.Symbols.Closure?(
358
enclosing.declare_closure(
359
location,
360
"$pack_thunk_{_index}",
361
cast Semantic.Scope?(_symbol_table.current_closure_context)!,
362
scope,
363
false,
364
null
365
)
366
)
367
368
_index = _index + 1
369
370
if !hoist? \/ !thunk? then
371
return null
372
fi
373
374
hoist.mark_synthesized()
375
hoist.define()
376
377
thunk.mark_synthesized()
378
379
_symbol_table.current_closure_context.add_closure(thunk)
380
381
let parts = THUNK_PARTS(thunk, hoist)
382
383
for_key[index] = parts
384
385
return parts
386
si
387
si
388
389
class THUNK_PARTS is
390
thunk: Semantic.Symbols.Closure
391
hoist: Semantic.Symbols.Variable
392
393
init(thunk: Semantic.Symbols.Closure, hoist: Semantic.Symbols.Variable) is
394
self.thunk = thunk
395
self.hoist = hoist
396
si
397
si
398
si