Skip to content
← Back

src/semantic/obligations.ghul

1
namespace Semantic is
2
use Source.LOCATION
3
4
use Collections.LIST
5
6
// A rule that could not fire on this walk because one of its inputs
7
// was still an inference placeholder: which rule, the placeholder it
8
// waits on, and where. Recorded instead of committing to a result
9
// computed from the settled inputs alone, or reporting the placeholder
10
// as an error the whole-body loop may yet erase.
11
class OBLIGATION(rule: string, awaiting: Types.Type, location: LOCATION?)
12
13
// The obligations recorded during the current walk of the body being
14
// compiled. The whole-body loop in COMPILE_LAMBDAS re-walks a body
15
// while its walks make progress, and every re-walk records the
16
// obligations still standing afresh, so the queue is cleared at each
17
// walk and read once the loop has stopped: an obligation still
18
// standing then, on a placeholder nothing settled, is reported at the
19
// placeholder's origin, naming the variable whose type the body could
20
// not infer.
21
//
22
// Recording an obligation marks the walk as having consumed an
23
// unresolved type, which is what keeps the loop walking; the sites
24
// that record one need not also signal that themselves.
25
class OBLIGATIONS is
26
_bodies: LIST[LIST[OBLIGATION]] static
27
28
init() static is
29
_bodies = LIST[LIST[OBLIGATION]]()
30
_previous = LIST[LIST[Symbols.Variable]]()
31
_defaulted = LIST[LIST[Symbols.Variable]]()
32
_learned = LIST[int]()
33
si
34
35
begin_body() static is
36
_bodies.add(LIST[OBLIGATION]())
37
_previous.add(LIST[Symbols.Variable]())
38
_defaulted.add(LIST[Symbols.Variable]())
39
_learned.add(0)
40
si
41
42
// Per body, the origins its previous walk left waiting, for
43
// `is_stuck` to compare against; a stack beside `_bodies`, since a
44
// body compiled during another's walk has walks of its own.
45
_previous: LIST[LIST[Symbols.Variable]] static
46
47
// Per body, how many bounds and constraints its current walk has
48
// recorded that were not recorded before. A walk that leaves the
49
// same obligations standing as the walk before it has still moved
50
// when this is not zero: what it learned is what the next walk
51
// settles the waiting origin from.
52
_learned: LIST[int] static
53
54
// Counts a bound or constraint that carried information, for every
55
// body currently being walked - a body compiled during another's
56
// walk is something that walk learned too.
57
note_learned() static is
58
for i in 0.._learned.count do
59
_learned[i] = _learned[i] + 1
60
od
61
si
62
63
// Per body, the origins the fixing step has taken a default for.
64
// Unlike the queue itself this survives `begin_walk`: the walk
65
// that follows the default is the walk that has to see it.
66
_defaulted: LIST[LIST[Symbols.Variable]] static
67
68
// The rule an untyped local initialized with an empty array
69
// literal waits under. The one rule with a default, so the one
70
// `take_defaults` answers.
71
EMPTY_LITERAL: string static => "empty_literal"
72
73
begin_walk() static is
74
if _bodies.count == 0 then
75
return
76
fi
77
78
let previous = LIST[Symbols.Variable]()
79
80
for obligation in standing do
81
if let placeholder: Types.INFERRED_VARIABLE_TYPE = obligation.awaiting then
82
if !previous.contains(placeholder.origin) then
83
previous.add(placeholder.origin)
84
fi
85
fi
86
od
87
88
_previous[_previous.count - 1] = previous
89
_bodies[_bodies.count - 1].clear()
90
_learned[_learned.count - 1] = 0
91
si
92
93
// Whether this walk left exactly the origins waiting that the
94
// previous walk did and learned nothing while doing it, so a
95
// further walk can only repeat this one.
96
is_stuck: bool static is
97
if _learned.count > 0 /\ _learned[_learned.count - 1] > 0 then
98
return false
99
fi
100
101
let current = LIST[Symbols.Variable]()
102
103
for obligation in standing do
104
if let placeholder: Types.INFERRED_VARIABLE_TYPE = obligation.awaiting then
105
if !current.contains(placeholder.origin) then
106
current.add(placeholder.origin)
107
fi
108
fi
109
od
110
111
let previous = if _previous.count > 0 then _previous[_previous.count - 1] else LIST[Symbols.Variable]() fi
112
113
if current.count != previous.count then
114
return false
115
fi
116
117
for origin in current do
118
if !previous.contains(origin) then
119
return false
120
fi
121
od
122
123
return true
124
si
125
126
end_body() static is
127
if _bodies.count > 0 then
128
_bodies.remove_at(_bodies.count - 1)
129
_previous.remove_at(_previous.count - 1)
130
_defaulted.remove_at(_defaulted.count - 1)
131
_learned.remove_at(_learned.count - 1)
132
fi
133
si
134
135
// The obligations the last walk of the current body recorded.
136
standing: LIST[OBLIGATION] static =>
137
if _bodies.count > 0 then _bodies[_bodies.count - 1] else LIST[OBLIGATION]() fi
138
139
defer(rule: string, awaiting: Types.Type, location: LOCATION?) static is
140
INFERENCE_TRACE.obligation(rule, awaiting, location)
141
142
if _bodies.count > 0 then
143
_bodies[_bodies.count - 1].add(OBLIGATION(rule, awaiting, location))
144
fi
145
146
IoC.CONTAINER.instance.logger.mark_consumed_any()
147
si
148
149
// Takes the language default for every standing obligation that
150
// has one, and says whether it took any. Called once the body's
151
// loop has stopped: a default is an answer to the walks having
152
// run out rather than information a walk found, so taking one
153
// earlier pre-empts the walk that would have settled the origin
154
// properly. The caller answers true with one more walk, which is
155
// where the default reaches the literal and everything typed
156
// from it.
157
take_defaults() -> bool static is
158
let taken mut = false
159
160
for obligation in standing do
161
if obligation.rule !~ EMPTY_LITERAL then
162
continue
163
fi
164
165
if let placeholder: Types.INFERRED_VARIABLE_TYPE = obligation.awaiting then
166
let origin = placeholder.origin
167
168
if let type = origin.type /\ type.is_settled then
169
continue
170
fi
171
172
let resolved = origin.try_get_inferred_type()
173
174
if resolved? /\ resolved.is_settled then
175
continue
176
fi
177
178
if has_default_for(origin) then
179
continue
180
fi
181
182
_defaulted[_defaulted.count - 1].add(origin)
183
184
taken = true
185
fi
186
od
187
188
return taken
189
si
190
191
// Whether the fixing step has taken a default for this origin, so
192
// the site that waited on it takes the default rather than waiting
193
// again.
194
has_default_for(origin: Symbols.Variable) -> bool static =>
195
_defaulted.count > 0 /\ _defaulted[_defaulted.count - 1].contains(origin)
196
197
// Called once the loop has stopped without converging. Each
198
// variable whose placeholder an obligation still waits on, and
199
// whose type nothing in the body settled, is reported once, at
200
// its declaration, pointing at the first site that waited on it.
201
// A constructor's type-argument phantom is reported by
202
// TYPE_ARG_PLACEHOLDER_REGISTRY instead, which knows the
203
// construction it belongs to.
204
fix(logger: Logging.Logger) static is
205
let reported = LIST[Symbols.Variable]()
206
207
for obligation in standing do
208
if let placeholder: Types.INFERRED_VARIABLE_TYPE = obligation.awaiting then
209
let origin = placeholder.origin
210
211
if isa Symbols.INFERRED_TYPE_ARG_ORIGIN(origin) \/ origin.name.starts_with("$") then
212
continue
213
fi
214
215
if reported.contains(origin) \/ has_default_for(origin) then
216
continue
217
fi
218
219
let resolved = origin.try_get_inferred_type()
220
221
if resolved? /\ resolved.is_settled then
222
continue
223
fi
224
225
if let type = origin.type /\ type.is_settled then
226
continue
227
fi
228
229
reported.add(origin)
230
231
if let location = _first_site_for(origin) then
232
logger.error(origin.location, "cannot infer the type of {origin.name}", location, "{origin.name} is needed here")
233
else
234
logger.error(origin.location, "cannot infer the type of {origin.name}")
235
fi
236
fi
237
od
238
si
239
240
// Where the first standing obligation on `origin` with a location
241
// was recorded.
242
_first_site_for(origin: Symbols.Variable) -> LOCATION? static is
243
for obligation in standing do
244
if let placeholder: Types.INFERRED_VARIABLE_TYPE = obligation.awaiting, location = obligation.location then
245
if placeholder.origin == origin then
246
return location
247
fi
248
fi
249
od
250
251
return null
252
si
253
si
254
si