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