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[string,Type] mut;
90
91
if is_direct \/ is_variant_of_constraint then
92
type_map = constraint_generic.type_map;
93
else
94
let unified = try_unify_via_ancestor(owner_classy, constraint_generic);
95
96
if !unified? then
97
return candidate;
98
fi
99
100
type_map = unified;
101
fi
102
103
let specialized_owner = Symbols.GENERIC.try_create_from(location, owner_classy, type_map);
104
105
if !specialized_owner? then
106
return candidate;
107
fi
108
109
let specialized = specialized_owner.find_specialized_function(candidate);
110
111
if !specialized? then
112
return candidate;
113
fi
114
115
return specialized;
116
si
117
118
// Walk owner_classy's ancestor types looking for a generic
119
// whose head matches `constraint_generic.symbol` and whose
120
// argument count matches. For the first match, build a map
121
// from owner_classy.argument_names to the constraint's
122
// actual arguments by lining up the ancestor's
123
// GenericArgument formals with positions in the constraint.
124
//
125
// Returns null when no ancestor matches or the alignment
126
// doesn't cover every owner name (e.g. the ancestor reads
127
// `Parent[(T, int)]` instead of `Parent[T]`).
128
//
129
// Exposed for unit testing in isolation; specialize_from_
130
// constraint calls it from the non-direct/non-variant path.
131
try_unify_via_ancestor(
132
owner_classy: Symbols.Classy,
133
constraint_generic: Symbols.GENERIC
134
) -> Collections.Map[string,Type]? is
135
for i in 0..owner_classy.ancestors.count do
136
let ancestor = owner_classy.ancestors[i];
137
138
if !isa Types.GENERIC(ancestor) then
139
continue;
140
fi
141
142
let ancestor_generic = ancestor;
143
let ancestor_symbol = cast Symbols.GENERIC?(ancestor_generic.symbol);
144
145
if
146
!ancestor_symbol? \/
147
!(ancestor_symbol.symbol =~ constraint_generic.symbol) \/
148
ancestor_generic.arguments.count != constraint_generic.arguments.count
149
then
150
continue;
151
fi
152
153
let candidate = Collections.MAP[string,Type]();
154
let ok mut = true;
155
156
for j in 0..ancestor_generic.arguments.count do
157
let slot = ancestor_generic.arguments[j];
158
159
if !isa Types.GenericArgument(slot) then
160
ok = false;
161
break;
162
fi
163
164
let arg = slot;
165
166
candidate[arg.name] = constraint_generic.arguments[j];
167
od
168
169
if !ok then
170
continue;
171
fi
172
173
for name in owner_classy.argument_names do
174
if !candidate.contains_key(name) then
175
ok = false;
176
break;
177
fi
178
od
179
180
if ok then
181
return candidate;
182
fi
183
od
184
185
return null;
186
si
187
si
188
si