Appearance
| 1 | namespace Semantic is | |
| 2 | use System.Exception; | |
| 3 | ||
| 4 | use Ghul.Pipes; | |
| 5 | ||
| 6 | use Logging; | |
| 7 | use Source; | |
| 8 | ||
| 9 | use Types.Type; | |
| 10 | ||
| 11 | // `score` is the summed per-argument match quality, kept for | |
| 12 | // ranking candidates against each other. `needs_retry` is the | |
| 13 | // explicit signal that a function-literal actual with implicit | |
| 14 | // parameter types matched without its real signature, so a | |
| 15 | // constraint-push re-walk of the arguments could improve the | |
| 16 | // result. Callers must consult `needs_retry` rather than testing | |
| 17 | // `score` against a MATCH constant: the sum of several coercion | |
| 18 | // scores can collide with any single MATCH value. | |
| 19 | class OVERLOAD_RESOLVE_RESULT(function: Symbols.Function, score: Types.MATCH, needs_retry: bool) is | |
| 20 | si | |
| 21 | ||
| 22 | class OVERLOAD_MATCHES_RESULT( | |
| 23 | results: Collections.List[Symbols.Function], | |
| 24 | best_result_index: int, | |
| 25 | current_parameter_index: int public | |
| 26 | ) is | |
| 27 | si | |
| 28 | ||
| 29 | class OVERLOAD_RESOLVER(_logger: Logger) is | |
| 30 | match_propagator: MATCH_PROPAGATOR public; | |
| 31 | _delegate_shape: DELEGATE_SHAPE; | |
| 32 | ||
| 33 | super(); | |
| 34 | ||
| 35 | init(..) is | |
| 36 | match_propagator = MATCH_PROPAGATOR(_logger); | |
| 37 | _delegate_shape = DELEGATE_SHAPE(); | |
| 38 | si | |
| 39 | ||
| 40 | resolve( | |
| 41 | location: LOCATION, | |
| 42 | group: Symbols.FUNCTION_GROUP, | |
| 43 | arguments: Collections.List[Type], | |
| 44 | want_infer: bool, | |
| 45 | want_instance: bool, | |
| 46 | is_constructor_call: bool | |
| 47 | ) -> OVERLOAD_RESOLVE_RESULT? => | |
| 48 | resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, null, null); | |
| 49 | ||
| 50 | // `restrict_to`, when non-null, limits resolution to the given | |
| 51 | // candidates instead of the whole group - used by named- | |
| 52 | // argument calls, which have already culled the group to the | |
| 53 | // overloads whose parameter names match. | |
| 54 | resolve( | |
| 55 | location: LOCATION, | |
| 56 | group: Symbols.FUNCTION_GROUP, | |
| 57 | arguments: Collections.List[Type], | |
| 58 | want_infer: bool, | |
| 59 | want_instance: bool, | |
| 60 | is_constructor_call: bool, | |
| 61 | restrict_to: Collections.List[Symbols.Function]? | |
| 62 | ) -> OVERLOAD_RESOLVE_RESULT? => | |
| 63 | resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, restrict_to, null); | |
| 64 | ||
| 65 | // `return_constraint`, when non-null, supplies the return-type | |
| 66 | // context (typically `function.return_type` of the enclosing | |
| 67 | // function for a call in return-position, or the LHS slot type | |
| 68 | // for an assignment-position call). Used as a tie-breaker when | |
| 69 | // arg-side scoring leaves multiple candidates tied: a candidate | |
| 70 | // whose return type is assignable to the constraint wins over | |
| 71 | // one that isn't. | |
| 72 | resolve( | |
| 73 | location: LOCATION, | |
| 74 | group: Symbols.FUNCTION_GROUP, | |
| 75 | arguments: Collections.List[Type], | |
| 76 | want_infer: bool, | |
| 77 | want_instance: bool, | |
| 78 | is_constructor_call: bool, | |
| 79 | restrict_to: Collections.List[Symbols.Function]?, | |
| 80 | return_constraint: Type? | |
| 81 | ) -> OVERLOAD_RESOLVE_RESULT? | |
| 82 | is | |
| 83 | let mark = _logger.mark(); | |
| 84 | ||
| 85 | try | |
| 86 | return _resolve(location, group, arguments, want_infer, want_instance, is_constructor_call, restrict_to, return_constraint); | |
| 87 | catch e: Exception | |
| 88 | _logger.release(mark); | |
| 89 | ||
| 90 | _logger.exception(location, e, "exception resolving overload: {group} arguments {arguments}"); | |
| 91 | return null; | |
| 92 | ||
| 93 | finally | |
| 94 | _logger.release(mark); | |
| 95 | yrt | |
| 96 | si | |
| 97 | ||
| 98 | find_matches( | |
| 99 | group: Symbols.FUNCTION_GROUP, | |
| 100 | arguments: Collections.List[Type] | |
| 101 | ) -> OVERLOAD_MATCHES_RESULT? | |
| 102 | is | |
| 103 | let mark = _logger.mark(); | |
| 104 | ||
| 105 | try | |
| 106 | return _find_matches(group, arguments); | |
| 107 | catch e: Exception | |
| 108 | _logger.release(mark); | |
| 109 | ||
| 110 | _logger.exception(null, e, "exception resolving overload: {group} arguments {arguments}"); | |
| 111 | return null; | |
| 112 | ||
| 113 | finally | |
| 114 | _logger.release(mark); | |
| 115 | yrt | |
| 116 | si | |
| 117 | ||
| 118 | _resolve( | |
| 119 | location: LOCATION, | |
| 120 | group: Symbols.FUNCTION_GROUP, | |
| 121 | arguments: Collections.List[Type], | |
| 122 | want_infer: bool, | |
| 123 | want_instance: bool, | |
| 124 | is_constructor_call: bool, | |
| 125 | restrict_to: Collections.List[Symbols.Function]?, | |
| 126 | return_constraint: Type? | |
| 127 | ) -> OVERLOAD_RESOLVE_RESULT? | |
| 128 | is | |
| 129 | if group == null \/ group.functions == null \/ arguments == null then | |
| 130 | return null; | |
| 131 | fi | |
| 132 | ||
| 133 | // FIXME: this could just as well be applied to any parameters of generic type, not just anon functions | |
| 134 | // an argument entry may be null | |
| 135 | @suppress("presence-test-non-optional") | |
| 136 | let needs_second_call = arguments |> any(a => a? /\ a.is_function_with_any_implicit_argument_types); | |
| 137 | ||
| 138 | let saw_delegate_target mut = false; | |
| 139 | ||
| 140 | let is_ambiguous mut = false; | |
| 141 | ||
| 142 | let best_score mut = cast int (Types.MATCH.DIFFERENT); | |
| 143 | let result: Symbols.Function? mut = _; | |
| 144 | ||
| 145 | let ambiguous_matches: Collections.LIST[Symbols.Function]? mut = null; | |
| 146 | ||
| 147 | let search_functions = if restrict_to? then restrict_to else group.functions fi; | |
| 148 | ||
| 149 | // A static constructor is invoked by the CLR, never by an | |
| 150 | // explicit call or `T(...)` construction, so it must not be | |
| 151 | // an overload candidate even though it shares the `init` | |
| 152 | // group with the instance constructors. | |
| 153 | let functions_to_search = search_functions |> filter(f => (want_instance \/ !f.is_instance) /\ !f.is_static_constructor); | |
| 154 | ||
| 155 | // We need to return PARTIAL if any actual argument types are wild. 'PARTIAL' provides | |
| 156 | // the caller with the best match we can find, and the caller is expected to use that | |
| 157 | // to bind any unknown types in the actual arguments and then try overload resolution | |
| 158 | // again | |
| 159 | ||
| 160 | for f in functions_to_search do | |
| 161 | let actual: Symbols.Function? mut = f; | |
| 162 | ||
| 163 | if f.arguments == null then | |
| 164 | return OVERLOAD_RESOLVE_RESULT(f, Types.MATCH.DIFFERENT, false); | |
| 165 | elif f.arguments.count == 0 /\ arguments.count == 0 then | |
| 166 | return OVERLOAD_RESOLVE_RESULT(f, Types.MATCH.SAME, false); | |
| 167 | elif f.arguments.count == arguments.count then | |
| 168 | let want_try_bind_owner_generic_arguments mut = false; | |
| 169 | let want_try_bind_function_generic_arguments mut = false; | |
| 170 | ||
| 171 | if is_constructor_call then | |
| 172 | want_try_bind_owner_generic_arguments = true; | |
| 173 | want_try_bind_function_generic_arguments = false; | |
| 174 | elif f.is_instance then | |
| 175 | // Iterative-inference: an instance method's | |
| 176 | // owner type-args may be unbound when the | |
| 177 | // receiver was constructed without explicit | |
| 178 | // type-args and without args binding T (e.g. | |
| 179 | // `let m = Box(); m.set(42)`). Allow owner- | |
| 180 | // generic-arg binding here so the resolver can | |
| 181 | // pick T = int from the actual argument and | |
| 182 | // produce the specialized owner. For receivers | |
| 183 | // whose type-args are already bound, the | |
| 184 | // specialization is a no-op. | |
| 185 | want_try_bind_owner_generic_arguments = true; | |
| 186 | want_try_bind_function_generic_arguments = true; | |
| 187 | elif f.is_generic then | |
| 188 | want_try_bind_owner_generic_arguments = true; | |
| 189 | want_try_bind_function_generic_arguments = true; | |
| 190 | else | |
| 191 | want_try_bind_owner_generic_arguments = true; | |
| 192 | want_try_bind_function_generic_arguments = false; | |
| 193 | fi | |
| 194 | ||
| 195 | let try_bind_generic_arguments mut = false; | |
| 196 | let score mut = cast int(Types.MATCH.SAME); | |
| 197 | ||
| 198 | let owner_symbol = cast Symbols.Classy?(f.owner)!; | |
| 199 | ||
| 200 | for i in 0..f.arguments.count do | |
| 201 | let match: Types.MATCH mut; | |
| 202 | ||
| 203 | let f_arg = f.arguments[i]; | |
| 204 | let arg = arguments[i]; | |
| 205 | ||
| 206 | // an argument may be null | |
| 207 | @suppress("presence-test-non-optional") | |
| 208 | if arg? then | |
| 209 | match = f_arg.compare(arg); | |
| 210 | ||
| 211 | // A function literal can be compiled as the | |
| 212 | // delegate this formal asks for, but it has | |
| 213 | // been walked as a plain function type and so | |
| 214 | // compares DIFFERENT. Report a partial match | |
| 215 | // instead: the retry pass pushes this formal | |
| 216 | // down and re-walks the argument, which either | |
| 217 | // produces the delegate or fails the second | |
| 218 | // resolve on its own terms. | |
| 219 | if | |
| 220 | match == Types.MATCH.DIFFERENT /\ | |
| 221 | arg.is_function /\ | |
| 222 | _delegate_shape.is_named_delegate(f_arg) | |
| 223 | then | |
| 224 | match = Types.MATCH.PARTIAL; | |
| 225 | saw_delegate_target = true; | |
| 226 | fi | |
| 227 | ||
| 228 | if match == Types.MATCH.DIFFERENT then | |
| 229 | // if any argument type compare returns DIFFERENT then | |
| 230 | // this overload cannot match the supplied arguments | |
| 231 | // even allowing for type argument inference of any | |
| 232 | // type arguments in the formal arguments or of | |
| 233 | // unknown types in the actual arguments, so | |
| 234 | // bail on this overload immediately: | |
| 235 | score = cast int(Types.MATCH.DIFFERENT); | |
| 236 | break; | |
| 237 | elif match == Types.MATCH.WILD then | |
| 238 | // either or both of the following has occurred: | |
| 239 | // 1. the formal argument type is 'wild', i.e. its type expression | |
| 240 | // includes at least one generic type argument that could be free | |
| 241 | // to be bound to the type in corresponding position in the actual | |
| 242 | // argument type. The type argument could be appear inside the type | |
| 243 | // expression at any depth, for example `List[T]` or `int -> T` | |
| 244 | // 2. the actual argument type is 'any', i.e. its type expression | |
| 245 | // includes at least one unknown type where the actual type can | |
| 246 | // potentially be inferred based on the formal argument type | |
| 247 | ||
| 248 | // we need first to figure out which it is. If it's both then type | |
| 249 | // inference probably isn't possible, but we will still attempt it | |
| 250 | ||
| 251 | if f_arg.is_wild then | |
| 252 | // this formal argument is wild, we need to figure out if any | |
| 253 | // type arguments in it could be free in this context | |
| 254 | ||
| 255 | // if the overload is an instance method we then can't supply | |
| 256 | // actual type parameters to its owning class/struct either explicitly | |
| 257 | // or via inference - they're already applied to the instance we're | |
| 258 | // calling the method on | |
| 259 | ||
| 260 | // if the overload is a static method, we can potentially supply | |
| 261 | // actual type arguments for its owning class | |
| 262 | ||
| 263 | // and in either case we can supply actual type arguments for the | |
| 264 | // method itself | |
| 265 | ||
| 266 | // if the function is a global function then we can supply actual | |
| 267 | // type arguments for it | |
| 268 | ||
| 269 | // if the function is a constructor, and we're calling it for a | |
| 270 | // constructor expression, it cannot have generic arguments but | |
| 271 | // its owning type can, and we do want to supply them if we | |
| 272 | // can infer them from this overload | |
| 273 | ||
| 274 | match = Types.MATCH.SAME; | |
| 275 | ||
| 276 | try_bind_generic_arguments = true; | |
| 277 | fi | |
| 278 | elif arg.is_error \/ arg.contains_inferred then | |
| 279 | // The actual argument is an ERROR/placeholder sentinel | |
| 280 | // or a composite carrying one inside. Treat as a | |
| 281 | // matching argument and see if that produces an | |
| 282 | // unambiguous overload result — if so the caller | |
| 283 | // uses the chosen overload's formal type to infer | |
| 284 | // back into the placeholder via match propagation. | |
| 285 | ||
| 286 | // FIXME not sure it makes sense to be setting score here - should be match | |
| 287 | score = cast int(Types.MATCH.PARTIAL); | |
| 288 | elif f_arg.is_wild /\ isa Types.NULL(arg) then | |
| 289 | // A wild optional formal (`T?`) trivially accepts a | |
| 290 | // bare null without pinning its type variable, so | |
| 291 | // compare returns a plain assignable match rather than | |
| 292 | // WILD. Still route through generic-argument binding: | |
| 293 | // the type variable is left free otherwise, and the | |
| 294 | // selected overload would emit with an unresolved `!!N` | |
| 295 | // that fails to load at run time. Errors and inference | |
| 296 | // sentinels are handled by the branch above; this is | |
| 297 | // only the genuine null literal. | |
| 298 | try_bind_generic_arguments = true; | |
| 299 | fi | |
| 300 | else | |
| 301 | // An actual or formal argument type is unresolved | |
| 302 | // (null) - reachable in analysis mode when an | |
| 303 | // expression's type has not yet been established. | |
| 304 | // Treat as a moderate-quality match so resolution | |
| 305 | // degrades gracefully instead of dereferencing null. | |
| 306 | match = Types.MATCH.ASSIGNABLE; | |
| 307 | fi | |
| 308 | ||
| 309 | score = cast int(score) + cast int(match); | |
| 310 | od | |
| 311 | ||
| 312 | if try_bind_generic_arguments /\ score <= best_score /\ score < cast int(Types.MATCH.DIFFERENT) then | |
| 313 | // if we saw any generic argument types in any of the function formal argument types | |
| 314 | // then we need to try to bind them to concrete types from the corresponding actual | |
| 315 | // argument types | |
| 316 | ||
| 317 | let function_generic_argument_bindings = | |
| 318 | if want_try_bind_function_generic_arguments then | |
| 319 | f.try_bind_generic_arguments(location, arguments); | |
| 320 | else | |
| 321 | null; | |
| 322 | fi; | |
| 323 | ||
| 324 | let owner_generic_argument_bindings = | |
| 325 | if want_try_bind_owner_generic_arguments then | |
| 326 | f.try_bind_owner_generic_arguments(location, arguments); | |
| 327 | else | |
| 328 | null | |
| 329 | fi; | |
| 330 | ||
| 331 | if function_generic_argument_bindings? then | |
| 332 | if function_generic_argument_bindings.is_bound then | |
| 333 | actual = f.specialize_function(function_generic_argument_bindings.map, null); | |
| 334 | elif needs_second_call then | |
| 335 | actual = f.specialize_function(function_generic_argument_bindings.map, null); | |
| 336 | score = cast int(Types.MATCH.PARTIAL); | |
| 337 | else | |
| 338 | score = cast int(Types.MATCH.DIFFERENT); | |
| 339 | fi | |
| 340 | elif owner_generic_argument_bindings? then | |
| 341 | if owner_generic_argument_bindings.is_bound then | |
| 342 | let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_symbol, owner_generic_argument_bindings.map); | |
| 343 | ||
| 344 | if specialized_owner? then | |
| 345 | actual = specialized_owner.find_specialized_function(f); | |
| 346 | else | |
| 347 | score = cast int(Types.MATCH.DIFFERENT); | |
| 348 | fi | |
| 349 | elif needs_second_call then | |
| 350 | let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_symbol, owner_generic_argument_bindings.map); | |
| 351 | ||
| 352 | if specialized_owner? then | |
| 353 | actual = specialized_owner.find_specialized_function(f); | |
| 354 | score = cast int(Types.MATCH.PARTIAL); | |
| 355 | else | |
| 356 | score = cast int(Types.MATCH.DIFFERENT); | |
| 357 | fi | |
| 358 | else | |
| 359 | score = cast int(Types.MATCH.DIFFERENT); | |
| 360 | fi | |
| 361 | else | |
| 362 | score = cast int(Types.MATCH.DIFFERENT); | |
| 363 | fi | |
| 364 | fi | |
| 365 | ||
| 366 | if score == best_score /\ score != cast int(Types.MATCH.DIFFERENT) /\ actual? then | |
| 367 | if !ambiguous_matches? then | |
| 368 | ambiguous_matches = Collections.LIST[Symbols.Function](); | |
| 369 | fi | |
| 370 | ||
| 371 | if ambiguous_matches.count == 0 /\ result? then | |
| 372 | ambiguous_matches.add(result); | |
| 373 | fi | |
| 374 | ||
| 375 | ambiguous_matches.add(actual); | |
| 376 | ||
| 377 | is_ambiguous = true; | |
| 378 | elif score < best_score /\ actual? then | |
| 379 | if ambiguous_matches? then | |
| 380 | ambiguous_matches.clear(); | |
| 381 | fi | |
| 382 | ||
| 383 | is_ambiguous = false; | |
| 384 | best_score = score; | |
| 385 | result = actual; | |
| 386 | fi | |
| 387 | fi | |
| 388 | od | |
| 389 | ||
| 390 | if is_ambiguous then | |
| 391 | let non_object_matches = | |
| 392 | ambiguous_matches |> | |
| 393 | filter(f => !f.arguments |> any(a => a.is_object)); | |
| 394 | ||
| 395 | let count = non_object_matches |> count(); | |
| 396 | ||
| 397 | if count == 1 then | |
| 398 | result = non_object_matches |> only(); | |
| 399 | is_ambiguous = false; | |
| 400 | elif count > 1 then | |
| 401 | ambiguous_matches = Collections.LIST(non_object_matches); | |
| 402 | fi | |
| 403 | fi | |
| 404 | ||
| 405 | // Return-type-context filter: when the caller supplied a | |
| 406 | // return-type constraint (e.g. a return statement whose | |
| 407 | // function returns `Tasks.TASK[int]`, or an assignment- | |
| 408 | // position call's LHS type), prefer candidates whose | |
| 409 | // return type is assignable to that constraint. Picks | |
| 410 | // between `from_exception(ex) -> Tasks.TASK` vs | |
| 411 | // `from_exception[T](ex) -> Tasks.TASK[T]` where T can | |
| 412 | // bind from the constraint — the latter is the | |
| 413 | // user's intent and the former would fail the return- | |
| 414 | // statement's own assignability check. Runs BEFORE the | |
| 415 | // non-generic filter so a generic candidate matching the | |
| 416 | // constraint wins over a non-generic one that doesn't. | |
| 417 | // | |
| 418 | // For a generic candidate whose return type carries type | |
| 419 | // variables that the constraint can pin (e.g. | |
| 420 | // `Tasks.TASK[T]` against `Tasks.TASK[int]`), specialize | |
| 421 | // the chosen function so its emitted return type binds T | |
| 422 | // from the constraint. Without this the caller would | |
| 423 | // accept the candidate as the right overload but reject | |
| 424 | // its result against the constraint at the next | |
| 425 | // assignability check. | |
| 426 | if is_ambiguous /\ return_constraint? /\ !return_constraint.is_sentinel then | |
| 427 | let constraint_matches = | |
| 428 | ambiguous_matches |> | |
| 429 | filter(f => RETURN_CONSTRAINT_FILTER.matches(f, return_constraint)); | |
| 430 | ||
| 431 | let count = constraint_matches |> count(); | |
| 432 | ||
| 433 | if count == 1 then | |
| 434 | result = constraint_matches |> only(); | |
| 435 | is_ambiguous = false; | |
| 436 | ||
| 437 | let specialized = RETURN_CONSTRAINT_FILTER.try_specialize(location, result, return_constraint); | |
| 438 | if specialized? then | |
| 439 | result = specialized; | |
| 440 | fi | |
| 441 | elif count > 1 then | |
| 442 | ambiguous_matches = Collections.LIST(constraint_matches); | |
| 443 | fi | |
| 444 | fi | |
| 445 | ||
| 446 | if is_ambiguous then | |
| 447 | // Prefer non-generic candidates over function-generic | |
| 448 | // ones (concrete `<>(int, int)` beats specialized | |
| 449 | // `<>[T: struct](T, T)` for `int <> int`). A concrete | |
| 450 | // candidate has no `specialized_from` link AND no | |
| 451 | // function-level type-arguments; a specialized form | |
| 452 | // of a function-generic has `specialized_from` set. | |
| 453 | let non_generic_matches = | |
| 454 | ambiguous_matches |> | |
| 455 | filter(f => | |
| 456 | !f.specialized_from? | |
| 457 | /\ f.generic_arguments.count == 0); | |
| 458 | ||
| 459 | let count = non_generic_matches |> count(); | |
| 460 | ||
| 461 | if count == 1 then | |
| 462 | result = non_generic_matches |> only(); | |
| 463 | is_ambiguous = false; | |
| 464 | elif count > 1 then | |
| 465 | ambiguous_matches = Collections.LIST(non_generic_matches); | |
| 466 | fi | |
| 467 | fi | |
| 468 | ||
| 469 | if result? /\ !is_ambiguous then | |
| 470 | // Back-feed the chosen overload's formal types as | |
| 471 | // constraints to any actual whose type is an | |
| 472 | // INFERRED_VARIABLE_TYPE placeholder. This is the | |
| 473 | // single primitive that drives iterative inference: | |
| 474 | // when a placeholder participates in overload | |
| 475 | // resolution and a concrete formal wins on the other | |
| 476 | // side, the formal becomes a constraint on the | |
| 477 | // placeholder's origin symbol. The retry loop in | |
| 478 | // COMPILE_EXPRESSIONS.visit(FUNCTION) then re-walks | |
| 479 | // the body with the narrowed type. Operators (which | |
| 480 | // are method calls in ghūl) flow through this path | |
| 481 | // for free. | |
| 482 | _propagate_chosen_match_args(result, arguments); | |
| 483 | ||
| 484 | if needs_second_call then | |
| 485 | best_score = cast int(Types.MATCH.PARTIAL); | |
| 486 | fi | |
| 487 | ||
| 488 | return OVERLOAD_RESOLVE_RESULT(result, cast Types.MATCH(best_score), needs_second_call \/ saw_delegate_target); | |
| 489 | fi | |
| 490 | ||
| 491 | if | |
| 492 | arguments |> any(a => a.is_error \/ (!want_infer /\ a.is_sentinel)) | |
| 493 | then | |
| 494 | return null; | |
| 495 | fi | |
| 496 | ||
| 497 | let tried = Collections.LIST[Symbols.Function](20); | |
| 498 | ||
| 499 | for f in functions_to_search do | |
| 500 | if f.arguments.count == arguments.count then | |
| 501 | tried.add(f); | |
| 502 | fi | |
| 503 | od | |
| 504 | ||
| 505 | let maybe_static mut = ""; | |
| 506 | ||
| 507 | if !want_instance then | |
| 508 | maybe_static = "static "; | |
| 509 | fi | |
| 510 | ||
| 511 | if is_ambiguous then | |
| 512 | _logger.error( | |
| 513 | location, | |
| 514 | "call is ambiguous {group.name}({arguments|}), tried {get_sorted_function_list_as_string(ambiguous_matches!)}" | |
| 515 | ); | |
| 516 | elif tried.count > 0 then | |
| 517 | _logger.error( | |
| 518 | location, | |
| 519 | "no {maybe_static}overload found for {group.name}({arguments|}), tried {get_sorted_function_list_as_string(tried)}" | |
| 520 | ); | |
| 521 | else | |
| 522 | _logger.error(location, "no {maybe_static}overload found for {group.name}({arguments|})"); | |
| 523 | fi | |
| 524 | ||
| 525 | return null; | |
| 526 | si | |
| 527 | ||
| 528 | get_sorted_function_list_as_string(functions: Collections.Iterable[Symbols.Function]) -> string static => | |
| 529 | (functions |> | |
| 530 | map(f => f.to_string()) |> | |
| 531 | sort()) | |
| 532 | .to_string() ?? ""; | |
| 533 | ||
| 534 | // For each (formal, actual) pair on the chosen overload, push | |
| 535 | // type-arg constraints into any INFERRED_VARIABLE_TYPE | |
| 536 | // placeholders found on either side. The accumulator is | |
| 537 | // retrieved on the next iteration's lambda arg-compile / con- | |
| 538 | // structor re-walk to resolve the placeholder to a concrete | |
| 539 | // type. When add_constraint returns true (a real new constraint | |
| 540 | // landed), signal progress to the retry loop via | |
| 541 | // mark_consumed_any. | |
| 542 | _propagate_chosen_match_args( | |
| 543 | chosen: Symbols.Function, | |
| 544 | actual_types: Collections.List[Type] | |
| 545 | ) is | |
| 546 | match_propagator.propagate_matches(chosen.arguments, actual_types); | |
| 547 | si | |
| 548 | ||
| 549 | _find_matches( | |
| 550 | group: Symbols.FUNCTION_GROUP, | |
| 551 | arguments: Collections.List[Type] | |
| 552 | ) -> OVERLOAD_MATCHES_RESULT? | |
| 553 | is | |
| 554 | if group == null \/ group.functions == null \/ arguments == null then | |
| 555 | return null; | |
| 556 | fi | |
| 557 | ||
| 558 | if group.functions.count == 0 then | |
| 559 | return null; | |
| 560 | fi | |
| 561 | ||
| 562 | if group.functions.count == 1 \/ arguments.count == 0 then | |
| 563 | return OVERLOAD_MATCHES_RESULT(group.functions, 0, -1); | |
| 564 | fi | |
| 565 | ||
| 566 | let results = Collections.LIST[Symbols.Function](); | |
| 567 | ||
| 568 | let best_score mut = cast int(Types.MATCH.DIFFERENT) * arguments.count; | |
| 569 | let best_index mut = -1; | |
| 570 | ||
| 571 | for f in group.functions do | |
| 572 | if f.arguments.count >= arguments.count then | |
| 573 | let score mut = cast int(Types.MATCH.SAME); | |
| 574 | ||
| 575 | for i in 0..arguments.count do | |
| 576 | let match: Types.MATCH mut; | |
| 577 | ||
| 578 | match = f.arguments[i].compare(arguments[i]); | |
| 579 | ||
| 580 | if match == Types.MATCH.DIFFERENT then | |
| 581 | score = cast int(Types.MATCH.DIFFERENT); | |
| 582 | fi | |
| 583 | ||
| 584 | score = score + cast int(match); | |
| 585 | od | |
| 586 | ||
| 587 | results.add(f); | |
| 588 | ||
| 589 | if score < best_score then | |
| 590 | best_score = score; | |
| 591 | best_index = results.count - 1; | |
| 592 | fi | |
| 593 | fi | |
| 594 | od | |
| 595 | ||
| 596 | return | |
| 597 | OVERLOAD_MATCHES_RESULT( | |
| 598 | results, | |
| 599 | best_index, | |
| 600 | -1 | |
| 601 | ); | |
| 602 | si | |
| 603 | si | |
| 604 | si |