Appearance
| 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 |