Appearance
| 1 | namespace Semantic is | |
| 2 | use Types.Type; | |
| 3 | ||
| 4 | // Looks up a .NET user-defined conversion operator (`op_Implicit` | |
| 5 | // or `op_Explicit`) reachable from a source or target type that | |
| 6 | // converts between the two. Reflection pulls these down as | |
| 7 | // `$op_implicit` / `$op_explicit` static methods - unreachable by | |
| 8 | // name from ghūl source since `$` is not an identifier character, | |
| 9 | // but addressable here because several overloads sharing that | |
| 10 | // name are distinguished only by return type, which `cast`'s | |
| 11 | // explicit target type supplies. | |
| 12 | class USER_DEFINED_CONVERSION_LOOKUP is | |
| 13 | init() is si | |
| 14 | ||
| 15 | find(source_type: Type, target_type: Type) -> Symbols.Function? is | |
| 16 | let target_non_optional = target_type.optional_inner_type ?? target_type; | |
| 17 | ||
| 18 | let candidates = Collections.LIST[Symbols.Function](); | |
| 19 | ||
| 20 | _collect_candidates(source_type, "$op_implicit", candidates); | |
| 21 | _collect_candidates(target_non_optional, "$op_implicit", candidates); | |
| 22 | _collect_candidates(source_type, "$op_explicit", candidates); | |
| 23 | _collect_candidates(target_non_optional, "$op_explicit", candidates); | |
| 24 | ||
| 25 | let best: Symbols.Function? mut = null; | |
| 26 | ||
| 27 | for f in candidates do | |
| 28 | if f.arguments.count != 1 \/ !f.return_type? then | |
| 29 | continue; | |
| 30 | fi | |
| 31 | ||
| 32 | if !(f.return_type.matches(target_non_optional)) then | |
| 33 | continue; | |
| 34 | fi | |
| 35 | ||
| 36 | let parameter_type = f.arguments[0]; | |
| 37 | ||
| 38 | if !parameter_type.is_assignable_from(source_type) then | |
| 39 | continue; | |
| 40 | fi | |
| 41 | ||
| 42 | if !best? \/ (parameter_type.matches(source_type) /\ !(best.arguments[0].matches(source_type))) then | |
| 43 | best = f; | |
| 44 | fi | |
| 45 | od | |
| 46 | ||
| 47 | return best; | |
| 48 | si | |
| 49 | ||
| 50 | _collect_candidates(owner_type: Type, name: string, into: Collections.LIST[Symbols.Function]) is | |
| 51 | let member = owner_type.find_member(name); | |
| 52 | ||
| 53 | if let group: Symbols.FUNCTION_GROUP = member then | |
| 54 | for f in group.functions do | |
| 55 | into.add(f); | |
| 56 | od | |
| 57 | elif let single: Symbols.Function = member then | |
| 58 | into.add(single); | |
| 59 | fi | |
| 60 | si | |
| 61 | si | |
| 62 | si |