Skip to content
← Back

src/semantic/argument_pack.ghul

1
namespace Semantic is
2
use Types.Type
3
4
// The shape rules for a formal declared to take an argument pack
5
// spread out - `f: T.. -> U`, or after parameters of its own,
6
// `f: (A, T..) -> A`.
7
//
8
// The pack binds to a positional tuple, so such a formal takes a
9
// function whose last parameter is that tuple. An actual that is a
10
// function of two or more parameters in its place describes the same
11
// call with the pack spread out, and is presented in the shape the
12
// formal asks for. One parameter there needs no presenting, and
13
// anything past the tuple limit has no tuple to bind to.
14
//
15
// Whether a formal was declared that way is recorded on its owning
16
// function; these rules answer only what the two shapes allow.
17
class ARGUMENT_PACK is
18
// The widest positional tuple there is, and so the widest call
19
// a pack can stand for.
20
MAXIMUM_ARITY: int static => 7
21
22
// How many parameters a function type takes. A void-returning
23
// one is an action, whose type arguments are its parameters
24
// alone; every other function type carries its return as the
25
// last of them.
26
parameter_count(function_type: Type) -> int static =>
27
if function_type.is_action then
28
function_type.arguments.count
29
else
30
function_type.arguments.count - 1
31
fi
32
33
// How many of a call's arguments the formal at `index` collects
34
// into the pack's tuple, and absent when the call is not one
35
// the spread applies to. One argument is the value itself and
36
// needs no tuple; past the tuple limit there is none to build.
37
spread_arity(index: int, argument_count: int) -> int? static is
38
if index < 0 \/ index >= argument_count then
39
return null
40
fi
41
42
let supplied = argument_count - index
43
44
if supplied < 2 \/ supplied > MAXIMUM_ARITY then
45
return null
46
fi
47
48
return supplied
49
si
50
51
// The function type the marker was written on: `depth` returns
52
// into the formal's own type, so zero is that type itself.
53
// Absent when the spine is shorter than the depth, or when what
54
// it arrives at is not the one-parameter shape a pack presents.
55
marked_slot(formal: Type?, depth: int) -> Type? static is
56
let current mut = formal
57
let remaining mut = depth
58
59
while remaining > 0 do
60
if !current? \/ !current.is_function \/ current.is_action then
61
return null
62
fi
63
64
current = current.arguments[current.arguments.count - 1]
65
remaining = remaining - 1
66
od
67
68
if is_pack_slot(current) then
69
return current
70
fi
71
72
return null
73
si
74
75
// The tuple a pack binds to when the call supplies this
76
// function: its parameters, in order. Absent where the actual
77
// is not the shape the adaptation applies to, or where a
78
// parameter is not settled enough to be a tuple element - an
79
// untyped literal says nothing about what the pack stands for,
80
// and a tuple of placeholders would be an answer rather than
81
// the question it still is.
82
packed_parameters(actual: Type?) -> Type? static => packed_parameters(actual, 0)
83
84
// The same tuple where the formal takes `fixed_count` parameters
85
// of its own before the pack: the actual's parameters after them.
86
packed_parameters(actual: Type?, fixed_count: int) -> Type? static is
87
if !actual? \/ !actual.is_function then
88
return null
89
fi
90
91
let arity = parameter_count(actual) - fixed_count
92
93
if arity < 2 \/ arity > MAXIMUM_ARITY then
94
return null
95
fi
96
97
let elements = Collections.LIST[Type]()
98
99
for i in fixed_count..fixed_count + arity do
100
let parameter = actual.arguments[i]
101
102
if
103
parameter.is_error \/
104
parameter.contains_inferred \/
105
parameter.is_wild
106
then
107
return null
108
fi
109
110
elements.add(parameter)
111
od
112
113
return IoC.CONTAINER.instance.innate_symbol_lookup.get_tuple_type(elements, null)
114
si
115
116
// The other reading of a shape that takes a pack as one tuple: a
117
// function of the tuple's elements. Absent where `shape` is not
118
// such a shape, or where the tuple is not settled enough to say
119
// what its elements are.
120
spread_shape(shape: Type?) -> Type? static is
121
if !is_pack_slot(shape) then
122
return null
123
fi
124
125
let fixed = fixed_count(shape!)
126
127
let tuple = shape.arguments[fixed]
128
129
if
130
!(tuple.is_value_tuple \/ isa Types.TUPLE(tuple)) \/
131
tuple.arguments.count < 2 \/
132
tuple.arguments.count > MAXIMUM_ARITY \/
133
tuple.contains_inferred
134
then
135
return null
136
fi
137
138
let types = Collections.LIST[Type]()
139
140
for i in 0..fixed do
141
types.add(shape.arguments[i])
142
od
143
144
for element in tuple.arguments do
145
types.add(element)
146
od
147
148
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
149
150
types.add(if shape.is_action then lookup.get_void_type() else shape.arguments[fixed + 1] fi)
151
152
return lookup.get_function_type(types, shape.is_pure_function)
153
si
154
155
// Whether this call shape is one a spread formal can present: a
156
// function whose last parameter is the tuple the pack binds to.
157
is_pack_slot(shape: Type?) -> bool static =>
158
shape? /\ shape.is_function /\ parameter_count(shape) >= 1
159
160
// How many parameters of its own a pack slot takes before the
161
// pack.
162
fixed_count(shape: Type) -> int static => parameter_count(shape) - 1
163
164
// The arity to adapt `argument_type` with to fill `shape`, and
165
// absent when the pair is not one the adaptation applies to.
166
adaptation_arity(shape: Type?, argument_type: Type?) -> int? static is
167
if !is_pack_slot(shape) then
168
return null
169
fi
170
171
if !argument_type? \/ !argument_type.is_function then
172
return null
173
fi
174
175
let arity = parameter_count(argument_type) - fixed_count(shape!)
176
177
if arity < 2 \/ arity > MAXIMUM_ARITY then
178
return null
179
fi
180
181
return arity
182
si
183
si
184
si