Skip to content
← Back

src/semantic/symbols/type_group.ghul

1
namespace Semantic.Symbols is
2
use System.Exception;
3
use System.NotImplementedException;
4
5
use IoC;
6
use Logging;
7
use Source;
8
9
use IR.Values.Value;
10
11
use Types.Type;
12
13
// Holds same-name types declared at different generic-argument counts
14
// under one identifier. `class Foo is` (0 arguments) and `class Foo[T] is`
15
// (1 argument) both live as direct members of the enclosing scope. A
16
// reflected `Foo` and `Foo`1` from the same .NET namespace join the
17
// group via the import side.
18
//
19
// The group is transparent to lookup paths that resolve to a single
20
// member: GENERIC_APPLICATION picks the member matching the supplied
21
// argument count; bare-name resolution picks the no-arguments member
22
// when one exists. Sites that genuinely care that they got a group
23
// (completion, hover) test `isa TYPE_GROUP`.
24
class TYPE_GROUP: Scoped is
25
_classies: Collections.LIST[Classy];
26
27
count: int => _classies.count;
28
is_empty: bool => _classies.count == 0;
29
30
short_description: string => name;
31
32
describe(context: DESCRIBE_CONTEXT) -> SignaturePart =>
33
PARTS.name(self);
34
35
describe_kind(context: DESCRIBE_CONTEXT) -> string? => "type group";
36
symbol_kind: SymbolKind => SymbolKind.CLASS;
37
completion_kind: CompletionKind => CompletionKind.CLASS;
38
39
is_type_group: bool => true;
40
is_type: bool => true;
41
42
classies: Collections.List[Classy] => _classies;
43
44
init(location: LOCATION, owner: Scope, name: string) is
45
super.init(location, owner, name);
46
47
_classies = Collections.LIST[Classy]();
48
si
49
50
add(classy: Classy) is
51
classy.has_argument_count_siblings = true;
52
_classies.add(classy);
53
si
54
55
remove(classy: Classy) is
56
_classies.remove(classy);
57
si
58
59
// Returns the member whose generic-argument count matches `count`,
60
// or null if no such member exists in the group.
61
find_by_generic_arguments_count(count: int) -> Classy? is
62
for c in _classies do
63
if c.argument_names.count == count then
64
return c;
65
fi
66
od
67
68
return null;
69
si
70
71
// The group's sole member with generic arguments, or null when there
72
// is not exactly one. The common reflected shape for a name shared
73
// across arities is one non-generic member (e.g. the static
74
// `KeyValuePair` factory class) plus one generic type
75
// (`KeyValuePair[K,V]`); a constructor call on the bare name resolves
76
// to that generic member so its arguments can be inferred.
77
sole_generic_member() -> Classy? is
78
let result: Classy? mut = null;
79
80
for c in _classies do
81
if c.argument_names.count > 0 then
82
if result? then
83
return null;
84
fi
85
86
result = c;
87
fi
88
od
89
90
return result;
91
si
92
93
// The generic-argument counts currently represented in the group,
94
// sorted ascending. Used for diagnostics ("expected 0 or 2 type
95
// arguments but found 1").
96
generic_arguments_counts: Collections.List[int] is
97
let result = Collections.LIST[int]();
98
99
for c in _classies do
100
result.add(c.argument_names.count);
101
od
102
103
result.sort();
104
105
return result;
106
si
107
108
// If the group holds exactly one member, return it; else return
109
// self. Callers that don't need group-awareness use this to
110
// transparently see a single Classy.
111
collapse_group_if_single_member() -> Symbol =>
112
if _classies.count == 1 then
113
_classies[0];
114
else
115
self;
116
fi;
117
118
find_member(name: string) -> Symbol? is
119
// Member lookup on a group falls through to the no-arguments
120
// member if one exists. Common case: `Foo.NESTED` when both
121
// `Foo` and `Foo[T]` exist and the user means the non-generic
122
// one.
123
let bare = find_by_generic_arguments_count(0);
124
125
if bare? then
126
return bare.find_member(name);
127
fi
128
129
return null;
130
si
131
132
// A bare-name load of a group means the no-arguments member.
133
// Resolution sites (`compile_access`, GENERIC_APPLICATION,
134
// `resolve_type_expressions`) normally collapse the group before
135
// it reaches a load, but this keeps a stray group from
136
// producing a `Load.SYMBOL` whose `type` cast fails — a group
137
// is not `Types.Typed`.
138
load(location: LOCATION, from: Value?, loader: SYMBOL_LOADER) -> Value is
139
let bare = find_by_generic_arguments_count(0);
140
141
if bare? then
142
return bare.load(location, from, loader);
143
fi
144
145
return IR.Values.DUMMY(Types.ERROR(), location);
146
si
147
148
to_string() -> string =>
149
"{description} [{_classies|}]";
150
si
151
si