Skip to content
← Back

src/syntax/process/compile-expressions/function_reference_adapter.ghul

1
namespace Syntax.Process is
2
use Source.LOCATION
3
4
use Semantic.Types.Type
5
6
// A function referred to by name becomes a delegate directly -
7
// `ldftn` on the method, no closure - so its type is fixed by its
8
// declaration. Where a formal's shape differs from it only in which
9
// carrier an optional position uses, no such delegate exists: the
10
// optional carriers convert at a value boundary, and a delegate's
11
// parameter and return are type arguments, where nothing converts.
12
//
13
// The reference is eta-expanded instead. `f` written into a
14
// `(T) -> U?` slot becomes the literal `(a) => f(a)`, spliced into
15
// the tree in the reference's place, and the ordinary literal path
16
// compiles it under the slot: the return boundary coerces one
17
// carrier into the other, and the delegate the slot binds against
18
// is the closure's own.
19
class FUNCTION_REFERENCE_ADAPTER(
20
_symbol_table: Semantic.SYMBOL_TABLE
21
) is
22
_index: int
23
24
super()
25
26
// Whether `source` - the shape a function reference walked to -
27
// and `target` - the formal it is going into - describe the same
28
// call, differing only in the optional carrier of one or more
29
// positions. A position still carrying an inference placeholder
30
// says nothing either way and is passed over; a position that
31
// disagrees on anything but the carrier rules the pair out, so
32
// an ordinary mismatch is still reported rather than adapted.
33
is_carrier_only_mismatch(source: Type?, target: Type?) -> bool static is
34
if !source? \/ !target? \/ !source.is_function \/ !target.is_function then
35
return false
36
fi
37
38
if source.arguments.count != target.arguments.count then
39
return false
40
fi
41
42
let last = source.arguments.count - 1
43
let any_carrier_pair mut = false
44
45
for i in 0..source.arguments.count do
46
let from = source.arguments[i]
47
let to = target.arguments[i]
48
49
if from.is_error \/ to.is_error then
50
return false
51
fi
52
53
if _is_carrier_pair(from, to) then
54
any_carrier_pair = true
55
continue
56
fi
57
58
if from.contains_inferred \/ to.contains_inferred \/ from.is_wild \/ to.is_wild then
59
continue
60
fi
61
62
// The return is covariant, every parameter
63
// contravariant, exactly as assignability reads them.
64
let compatible =
65
if i == last then
66
to.is_assignable_from(from)
67
else
68
from.is_assignable_from(to)
69
fi
70
71
if !compatible then
72
return false
73
fi
74
od
75
76
return any_carrier_pair
77
si
78
79
// Two optional types over the same inner type held in different
80
// carriers: `MAYBE[T]` against a reference `T?` or a
81
// `Nullable[T]`. Both directions, since a reference may be going
82
// either way across the boundary.
83
_is_carrier_pair(left: Type, right: Type) -> bool static is
84
if !left.is_optional \/ !right.is_optional then
85
return false
86
fi
87
88
if left.is_maybe == right.is_maybe then
89
return false
90
fi
91
92
let left_inner = left.optional_inner_type
93
let right_inner = right.optional_inner_type
94
95
if !left_inner? \/ !right_inner? then
96
return false
97
fi
98
99
// A type variable or a placeholder at the inner position
100
// says nothing about whether the two agree - the carrier
101
// is what is being compared, and it already differs.
102
return
103
left_inner.contains_inferred \/
104
right_inner.contains_inferred \/
105
left_inner.is_wild \/
106
right_inner.is_wild \/
107
left_inner.is_equivalent_to(right_inner)
108
si
109
110
// Builds `(a0, .., an) => reference(a0, .., an)` over the
111
// reference, and declares the closure the literal needs. The
112
// caller puts the literal where the reference stood - a call
113
// holds one in its argument list by index, an operator in a
114
// named slot - and re-walks it in the reference's place. Null
115
// when the reference is not one this can wrap.
116
try_adapt(
117
reference: Trees.Expressions.Expression,
118
arity: int
119
) -> Trees.Expressions.FUNCTION? is
120
if isa Trees.Expressions.FUNCTION(reference) then
121
return null
122
fi
123
124
let enclosing = _symbol_table.current_function
125
126
if !enclosing? then
127
return null
128
fi
129
130
let location = reference.location
131
132
let formals = Collections.LIST[Trees.Expressions.Expression]()
133
let actuals = Collections.LIST[Trees.Expressions.Expression]()
134
let names = Collections.LIST[string]()
135
136
for i in 0..arity do
137
let name = "$adapted_{_index}_{i}"
138
139
names.add(name)
140
141
formals.add(
142
Trees.Expressions.VARIABLE(
143
location,
144
Trees.Identifiers.Identifier(location, name),
145
Trees.TypeExpressions.INFER(location),
146
null
147
)
148
)
149
150
actuals.add(
151
Trees.Expressions.IDENTIFIER(
152
location,
153
Trees.Identifiers.Identifier(location, name)
154
)
155
)
156
od
157
158
// The reference itself becomes the literal's callee, so it
159
// is walked once, in its new home. Whatever the earlier walk
160
// pushed onto it was a function-type slot it no longer sits
161
// in.
162
reference.set_expected_type(null, null)
163
164
let call =
165
Trees.Expressions.CALL(
166
location,
167
reference,
168
Trees.Expressions.LIST(location, actuals)
169
)
170
171
let body = Trees.Bodies.EXPRESSION(location, call)
172
173
let literal =
174
Trees.Expressions.FUNCTION(
175
location,
176
Trees.Expressions.LIST(location, formals),
177
Trees.TypeExpressions.INFER(location),
178
body,
179
false
180
)
181
182
let closure =
183
cast Semantic.Symbols.Closure?(
184
enclosing.declare_closure(
185
location,
186
"$adapted_{_index}",
187
cast Semantic.Scope?(_symbol_table.current_closure_context)!,
188
_symbol_table.current_scope,
189
false,
190
null
191
)
192
)
193
194
_index = _index + 1
195
196
if !closure? then
197
return null
198
fi
199
200
closure.mark_synthesized()
201
202
closure.is_carrier_adapter = true
203
204
_symbol_table.associate_node_with_scope(literal, closure)
205
206
closure.start_declaring_arguments()
207
208
for name in names do
209
closure.declare_variable(location, name, false, null).mark_synthesized()
210
od
211
212
closure.end_declaring_arguments()
213
214
// The body is a scope carrier in its own right, as it is
215
// under any other literal.
216
_symbol_table.associate_node_with_scope(body, Semantic.BLOCK_SCOPE(closure))
217
218
return literal
219
si
220
si
221
si