Skip to content
← Back

src/semantic/owner_constraint_specializer.ghul

1
namespace Semantic is
2
use Source.LOCATION
3
4
use Types.Type
5
6
// Constraint-driven owner specialisation. Companion to
7
// OWNER_TYPE_ARG_SPECIALIZER (which binds owner-generic args
8
// from sibling actuals); this one binds from a downstream type
9
// constraint already pushed onto the constructor expression.
10
//
11
// Three cases produce a binding:
12
//
13
// 1. Direct: the constraint's head Classy is the candidate's
14
// owner. `let b: Box[int] = Box()` — constraint is
15
// `Box[int]`, owner is `Box`, type_map comes straight from
16
// the constraint's `type_map`.
17
//
18
// 2. Variant of constraint: the candidate's owner is a variant
19
// of the constraint's union. `let m = MAYBE.SOME(42)` with
20
// constraint `MAYBE[int]` — SOME inherits its parent's
21
// argument_names (declare_variant passes them through), so
22
// the union-keyed type_map's keys line up with the variant's
23
// slots.
24
//
25
// 3. Ancestor unification: the constraint's head is an ancestor
26
// of the candidate's owner. `class C[T]: Parent[T]` then
27
// `let p: Parent[int] = C()`. We walk owner_classy's
28
// ancestors; for any that's a generic of the constraint's
29
// head with matching arity, line up ancestor formal slots
30
// with constraint actuals and build a type_map keyed by
31
// owner_classy's argument_names.
32
//
33
// The helper is stateless. Callers pass (candidate, constraint,
34
// location) and get back either a specialised function or the
35
// original candidate. Returning the original on any failure path
36
// — not null — means the caller can chain
37
// `_try_specialize_owner_with_placeholders` immediately after
38
// without re-checking.
39
class OWNER_CONSTRAINT_SPECIALIZER open is
40
init() is
41
super.init()
42
si
43
44
specialize_from_constraint(
45
location: LOCATION,
46
candidate: Symbols.Function,
47
constraint: Type?
48
) -> Symbols.Function is
49
if !constraint? then
50
return candidate
51
fi
52
53
// The constraint was pushed down from an enclosing
54
// expression on an earlier iteration and may embed
55
// placeholders whose origins have since settled.
56
// Specialising from the stale composite would commit a
57
// placeholder-bearing owner - and an owner that is
58
// already a Symbols.GENERIC short-circuits the phantom
59
// registry, so nothing downstream would refresh it.
60
let constraint_resolved = SETTLED_PLACEHOLDER_RESOLVER.instance.resolve(constraint)
61
62
if isa Symbols.GENERIC(candidate.owner) then
63
return candidate
64
fi
65
66
let owner_classy = cast Symbols.Classy?(candidate.owner)
67
68
if !owner_classy? \/ !owner_classy.is_generic then
69
return candidate
70
fi
71
72
let constraint_named = cast Types.NAMED?(constraint_resolved)
73
74
if !constraint_named? then
75
return candidate
76
fi
77
78
let constraint_generic = cast Symbols.GENERIC?(constraint_named.symbol)
79
80
if !constraint_generic? then
81
return candidate
82
fi
83
84
let is_direct = constraint_generic.symbol =~ owner_classy
85
let is_variant_of_constraint =
86
owner_classy.is_variant /\
87
cast Symbols.Symbol?(owner_classy.owner)! =~ constraint_generic.symbol
88
89
let type_map: Collections.Map[Symbols.Symbol,Type] mut
90
91
if is_direct then
92
type_map = constraint_generic.type_map
93
elif is_variant_of_constraint then
94
// A variant declares type parameters of its own, distinct
95
// from the union's even though it is declared with the
96
// union's names. The constraint's map is keyed on the
97
// union's parameters, so it is re-keyed onto the
98
// variant's by position - the variant's slot i is the
99
// union's slot i, which is what declare_variant passing
100
// the union's names through means.
101
let variant_map = Collections.MAP[Symbols.Symbol,Type]()
102
103
for i in 0..owner_classy.argument_names.count do
104
if i >= constraint_generic.arguments.count then
105
return candidate
106
fi
107
108
let parameter = owner_classy.type_parameter_at(i)
109
110
if !parameter? then
111
return candidate
112
fi
113
114
variant_map[parameter] = constraint_generic.arguments[i]
115
od
116
117
type_map = variant_map
118
else
119
let unified = try_unify_via_ancestor(owner_classy, constraint_generic)
120
121
if !unified? then
122
return candidate
123
fi
124
125
type_map = unified
126
fi
127
128
let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_classy, type_map)
129
130
if !specialized_owner? then
131
return candidate
132
fi
133
134
let specialized = specialized_owner.find_specialized_function(candidate)
135
136
if !specialized? then
137
return candidate
138
fi
139
140
return specialized
141
si
142
143
// Walk owner_classy's ancestor types looking for a generic
144
// whose head matches `constraint_generic.symbol` and whose
145
// argument count matches. For the first match, build a map
146
// from owner_classy.argument_names to the constraint's
147
// actual arguments by lining up the ancestor's
148
// GenericArgument formals with positions in the constraint.
149
//
150
// Returns null when no ancestor matches or the alignment
151
// doesn't cover every owner name (e.g. the ancestor reads
152
// `Parent[(T, int)]` instead of `Parent[T]`).
153
//
154
// Exposed for unit testing in isolation; specialize_from_
155
// constraint calls it from the non-direct/non-variant path.
156
try_unify_via_ancestor(
157
owner_classy: Symbols.Classy,
158
constraint_generic: Symbols.GENERIC
159
) -> Collections.Map[Symbols.Symbol,Type]? is
160
for i in 0..owner_classy.ancestors.count do
161
let ancestor = owner_classy.ancestors[i]
162
163
if !isa Types.GENERIC(ancestor) then
164
continue
165
fi
166
167
let ancestor_generic = ancestor
168
let ancestor_symbol = cast Symbols.GENERIC?(ancestor_generic.symbol)
169
170
if
171
!ancestor_symbol? \/
172
!(ancestor_symbol.symbol =~ constraint_generic.symbol) \/
173
ancestor_generic.arguments.count != constraint_generic.arguments.count
174
then
175
continue
176
fi
177
178
let candidate = Collections.MAP[Symbols.Symbol,Type]()
179
let ok mut = true
180
181
for j in 0..ancestor_generic.arguments.count do
182
let slot = ancestor_generic.arguments[j]
183
184
if !isa Types.GenericArgument(slot) then
185
ok = false
186
break
187
fi
188
189
let arg = slot
190
191
candidate[arg.symbol] = constraint_generic.arguments[j]
192
od
193
194
if !ok then
195
continue
196
fi
197
198
for i in 0..owner_classy.argument_names.count do
199
let parameter = owner_classy.type_parameter_at(i)
200
201
if !parameter? \/ !candidate.contains_key(parameter) then
202
ok = false
203
break
204
fi
205
od
206
207
if ok then
208
return candidate
209
fi
210
od
211
212
return null
213
si
214
si
215
si