Skip to content
← Back

src/semantic/user_defined_conversion_lookup.ghul

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