Skip to content
← Back

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

1
namespace Syntax.Process is
2
use System.Exception
3
4
use Logging
5
6
use Semantic.LEAST_UPPER_BOUND_MAP
7
use Semantic.Types.Type
8
9
use IR.Values
10
use IR.VALUE_BOXER
11
12
use Ghul.Pipes
13
14
// Compiles tuple and list/array (`SEQUENCE`) literal expressions.
15
// Split out of COMPILE_EXPRESSIONS, which delegates pre(tuple),
16
// visit(tuple) and the inner sequence compile here. The
17
// exception-handling and walk orchestration for visit(sequence)
18
// stays on the visitor; visit_sequence is the enclosed logic.
19
class COMPILE_TUPLES is
20
_logger: Logger
21
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
22
_value_boxer: VALUE_BOXER
23
_visitor: COMPILE_EXPRESSIONS
24
_placeholder_resolver: Semantic.SETTLED_PLACEHOLDER_RESOLVER
25
_match_propagator: Semantic.MATCH_PROPAGATOR
26
27
init(
28
logger: Logger,
29
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
30
value_boxer: VALUE_BOXER,
31
visitor: COMPILE_EXPRESSIONS
32
) is
33
super.init()
34
35
_logger = logger
36
_innate_symbol_lookup = innate_symbol_lookup
37
_value_boxer = value_boxer
38
_visitor = visitor
39
_placeholder_resolver = Semantic.SETTLED_PLACEHOLDER_RESOLVER()
40
_match_propagator = Semantic.MATCH_PROPAGATOR(logger)
41
si
42
43
pre_tuple(tuple: Trees.Expressions.TUPLE) -> bool is
44
if tuple.elements.expressions.count == 1 then
45
// Single-element TUPLE is a parenthesised expression
46
// (TUPLE.visit unwraps its value). Forward whatever
47
// constraint applies to the outer expression directly
48
// to the inner one — splitting a value-tuple constraint
49
// across a phantom 1-tuple slot would push the wrong
50
// element type down into the inner expression.
51
if tuple.expected_type? then
52
let forward_pack = tuple.compile_expressions_state.pack_marker
53
54
tuple.elements.expressions[0].set_expected_type(tuple.expected_type, tuple.expected_type_error_message)
55
56
if forward_pack > 0 then
57
tuple.set_expects_pack(forward_pack - 1)
58
fi
59
else
60
tuple.elements.expressions[0].clear_expected_type()
61
fi
62
elif let tuple.expected_type? /\ expected_type.is_value_tuple then
63
for (element, type) in tuple.elements |> zip(expected_type.arguments) do
64
element.set_expected_type(type, tuple.expected_type_error_message)
65
od
66
else
67
for element in tuple.elements do
68
element.clear_expected_type()
69
od
70
fi
71
72
return false
73
si
74
75
visit_tuple(tuple: Trees.Expressions.TUPLE) is
76
// A type ascription on a parenthesised group only means
77
// something when the group turns out to be a lambda's
78
// destructured parameter, which consumes it before any
79
// value is compiled. Reaching here with one still attached
80
// means the group is an ordinary tuple value, where the
81
// ascription has no effect and would otherwise be dropped
82
// in silence.
83
if let tuple.type_expression? then
84
_logger.error(type_expression.location, "type not allowed here")
85
fi
86
87
let names: Collections.LIST[string]? mut = Collections.LIST[string]()
88
let values = Collections.LIST[Value]()
89
let types = Collections.LIST[Type]()
90
91
let element_constraints: Collections.List[Type]? mut = null
92
let expected_type_error_message: string? mut = null
93
94
if let tuple.expected_type? /\ expected_type.is_value_tuple then
95
element_constraints = expected_type.arguments
96
expected_type_error_message = tuple.expected_type_error_message
97
fi
98
99
if tuple.elements.expressions.count == 1 then
100
// A single parenthesised element is a grouped expression,
101
// not a tuple — there are no single-element tuples (a bare
102
// `(x)` is just `x`). A named/typed sole element (`(x = 1)`
103
// / `(x: int)`) is an attempt at one; report it rather than
104
// collapsing to the element's unset value, which would
105
// poison to ERROR and crash IL emission far downstream.
106
if let element: Trees.Expressions.TUPLE_ELEMENT = tuple.elements.expressions[0] then
107
_logger.error(element.location, "single-element tuples are not supported; a tuple needs two or more elements")
108
tuple.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), tuple.location)
109
else
110
tuple.compile_expressions_state.value = tuple.elements.expressions[0].value
111
fi
112
return
113
elif tuple.elements.expressions.count == 0 then
114
tuple.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), tuple.location)
115
116
_logger.error(tuple.location, "empty tuple")
117
return
118
elif tuple.elements.expressions.count > Semantic.Lookups.INNATE_TYPE_LIMITS.MAX_TUPLE_ELEMENTS then
119
tuple.compile_expressions_state.value = IR.Values.DUMMY(Semantic.Types.ERROR(), tuple.location)
120
121
_logger.error(
122
tuple.location,
123
"a tuple literal cannot have more than {Semantic.Lookups.INNATE_TYPE_LIMITS.MAX_TUPLE_ELEMENTS} elements")
124
return
125
fi
126
127
let seen_any_named mut = false
128
129
for (index, v) in tuple.elements |> index() do
130
let element_type_constraint =
131
if element_constraints? then
132
element_constraints[index]
133
else
134
null
135
fi
136
137
if isa Trees.Expressions.TUPLE_ELEMENT(v) then
138
let element = v
139
140
seen_any_named = true
141
names.add(element.name.name)
142
143
// An attribute pragma only makes sense on a lambda
144
// parameter — reaching here means the parenthesised
145
// list was never rewritten into one (no `->`/`=>`/
146
// `is`/`rec` followed), so this is a genuine tuple
147
// literal instead.
148
if element.pragmas? then
149
for pragma in element.pragmas do
150
_logger.error(pragma.location, "attribute is not allowed here")
151
od
152
fi
153
154
if element.initializer? then
155
if Value.check_is_consumable(_logger, element.initializer.location, element.initializer.value) then
156
if element.type_expression.type? then
157
let expr_type = element.type_expression.type
158
let type =
159
if element_type_constraint? then
160
element_type_constraint
161
else
162
expr_type
163
fi
164
165
let init_value = element.initializer!.value!
166
167
if element.type_expression.check_is_not_reference(_logger, "tuple element cannot be a reference") then
168
if !type.is_assignable_from(init_value.type!) then
169
_logger.error(element.location, "{init_value.type} is not assignable to {type}")
170
elif element_type_constraint? /\ !element_type_constraint.is_assignable_from(expr_type) then
171
_logger.error(
172
element.type_expression.location,
173
string.format(expected_type_error_message!, [expr_type, element_type_constraint]:object))
174
fi
175
fi
176
177
types.add(type)
178
values.add(_value_boxer.box_if_needed(init_value, type))
179
180
elif element_type_constraint? then
181
let type = element_type_constraint
182
let init_value = element.initializer!.value!
183
184
if !type.is_assignable_from(init_value.type!) then
185
_logger.error(
186
element.location,
187
string.format(expected_type_error_message!, [init_value.type!, type]:object))
188
fi
189
190
types.add(type)
191
values.add(_value_boxer.box_if_needed(init_value, type))
192
else
193
let init_value = element.initializer!.value!
194
types.add(init_value.type!)
195
values.add(init_value)
196
fi
197
198
continue
199
fi
200
else
201
_logger.error(element.location, "tuple element must have a value")
202
fi
203
204
values.add(IR.Values.DUMMY(Semantic.Types.ERROR(), element.location))
205
types.add(Semantic.Types.ERROR())
206
207
elif Value.check_is_consumable(_logger, v.location, v.value) then
208
let v_value = v.value!
209
210
if v.is_identifier then
211
// An unnamed element that is a plain identifier
212
// takes its name from that identifier: `(a, b)`
213
// is `(a = a, b = b)`. TUPLE_ELEMENT_NAME owns
214
// the underscore-strip corner cases.
215
let identifier_name = v.try_copy_as_identifer()!.name
216
let is_field_symbol =
217
isa IR.Values.Load.SYMBOL(v_value) /\
218
v_value.has_symbol /\
219
v_value.symbol.is_field
220
221
names.add(TUPLE_ELEMENT_NAME.infer(identifier_name, is_field_symbol))
222
seen_any_named = true
223
else
224
names.add("{index}")
225
fi
226
227
if element_type_constraint? then
228
let type = element_type_constraint
229
230
if !type.is_assignable_from(v_value.type!) then
231
_logger.error(
232
v.location,
233
string.format(expected_type_error_message!, [v_value.type!, type]:object))
234
fi
235
236
types.add(type)
237
values.add(_value_boxer.box_if_needed(v_value, type))
238
else
239
types.add(v_value.type!)
240
values.add(v_value)
241
fi
242
else
243
values.add(IR.Values.DUMMY(Semantic.Types.ERROR(), v.location))
244
types.add(Semantic.Types.ERROR())
245
fi
246
247
debug_unindent()
248
od
249
250
if !seen_any_named then
251
names = null
252
fi
253
254
let type = _innate_symbol_lookup.get_tuple_type(_placeholder_resolver.resolve_all(types), names)
255
tuple.compile_expressions_state.value = TUPLE(type, values)
256
si
257
258
visit_sequence(sequence: Trees.Expressions.SEQUENCE) is
259
let type: Type? mut = _
260
let have_explicit_type: bool mut = _
261
262
let elements = Collections.LIST[Trees.Expressions.Expression](sequence.elements)
263
264
let constraint_type: Type? mut = _
265
if let sequence.expected_type? then
266
constraint_type = expected_type.get_element_type()
267
fi
268
269
if !isa Trees.TypeExpressions.INFER(sequence.type_expression) then
270
if sequence.type_expression.type? then
271
type = sequence.type_expression.type
272
273
have_explicit_type = true
274
else
275
// FIXME probably not needed - type pass will have given an error
276
_logger.error(sequence.type_expression.location, "bad explicit type expression")
277
return
278
fi
279
elif elements.count == 0 then
280
if constraint_type? then
281
type = constraint_type
282
have_explicit_type = true
283
else
284
// No elements to infer from and no constraint pushed by
285
// the surrounding context: fall back to an object
286
// element type, the same as a literal whose elements
287
// are all null.
288
_logger.hint(
289
sequence.location,
290
"infer-object-from-list-literal",
291
"list literal element type inferred as object",
292
sequence.location,
293
"help: give the literal an explicit type")
294
295
type = _innate_symbol_lookup.get_object_type()
296
fi
297
else
298
let seen_any mut = false
299
let seen_error mut = false
300
let all_tuple_literals mut = elements.count > 0 // assume all tuples until proved otherwise
301
let tuples_element_count mut = -1
302
let any_tuple_argument_is_null mut = false
303
let lub = LEAST_UPPER_BOUND_MAP()
304
let seen_empty_literal mut = false
305
306
for v in elements do
307
// An empty literal with nothing around it to say what
308
// it holds takes its type from its siblings, so it
309
// stays out of their join and is re-walked at the
310
// element type they settle on.
311
if CONTEXTLESS_EMPTY_LITERAL.is_expression(v) then
312
seen_empty_literal = true
313
all_tuple_literals = false
314
continue
315
fi
316
317
let is_tuple =
318
v.is_tuple_literal \/
319
(v.value? /\ v.value.type? /\ (v.value.type.is_value_tuple \/ isa Semantic.Types.TUPLE(v.value.type)) /\ !v.value.type.is_optional)
320
321
if !is_tuple then
322
all_tuple_literals = false
323
fi
324
325
if let v.value? /\ value.type? then
326
if value.check_is_consumable(_logger, v.location) then
327
if value.type!.is_inferred then
328
// A placeholder answers `is_null`, so it is
329
// added to the join here, ahead of the null
330
// literal's own handling.
331
lub.add(value.type!)
332
elif !value.type!.is_null then
333
seen_any = true
334
lub.add(value.type!)
335
elif value.type!.is_error then
336
seen_error = true
337
fi
338
339
if is_tuple then
340
if tuples_element_count == -1 then
341
tuples_element_count = value.type!.arguments.count
342
elif value.type!.arguments.count != tuples_element_count then
343
all_tuple_literals = false
344
fi
345
346
// `Types.NULL.matches` answers true against
347
// any type, so a naive structural LUB over
348
// the whole tuple types below judges a
349
// `null`-carrying tuple argument position
350
// "the same as" a value-carrying one at a
351
// sibling element and picks one arbitrarily,
352
// instead of widening that position to its
353
// optional carrier. Route around the naive
354
// LUB and straight to the per-position
355
// TUPLE_ELEMENT_LUB whenever that can happen.
356
if value.type!.arguments |> any(a => a.is_null) then
357
any_tuple_argument_is_null = true
358
fi
359
fi
360
else
361
seen_error = true
362
fi
363
else
364
seen_error = true
365
fi
366
od
367
368
// With no sibling to take a type from, the empty literals
369
// join as they are.
370
if seen_empty_literal /\ !seen_any then
371
for v in elements do
372
if CONTEXTLESS_EMPTY_LITERAL.is_expression(v) /\ v.value? /\ v.value.type? then
373
seen_any = true
374
lub.add(v.value.type)
375
fi
376
od
377
378
seen_empty_literal = false
379
fi
380
381
if !type? then
382
type = lub.get_result()
383
384
// The join waits for an element whose type is still
385
// being inferred - a local assigned later in the body -
386
// so the literal waits with it rather than settling
387
// whatever it is assigned to at a type that element may
388
// not fit.
389
if type? /\ type.is_inferred /\ !constraint_type? then
390
_logger.mark_consumed_any()
391
_logger.error(sequence.location, "cannot infer type here")
392
393
sequence.compile_expressions_state.value = DUMMY(_innate_symbol_lookup.get_array_type(Semantic.Types.ERROR()), sequence.location)
394
return
395
fi
396
397
if all_tuple_literals /\ (!type? \/ !type.is_value_tuple \/ any_tuple_argument_is_null) then
398
let tuple_types = Collections.LIST[Type]()
399
400
for element in elements do
401
if let element.value? /\ value.type? then
402
tuple_types.add(value.type)
403
fi
404
od
405
406
// The join builds a tuple of as many elements as
407
// the widest element has, so an element that is
408
// not a usable tuple - one already reported, or
409
// one past the element limit - has no tuple type
410
// to join into. The element's own diagnostic is
411
// the one worth keeping.
412
if
413
tuple_types |> any(t =>
414
t.is_error \/
415
t.arguments.count == 0 \/
416
t.arguments.count >
417
Semantic.Lookups.INNATE_TYPE_LIMITS.MAX_TUPLE_ELEMENTS)
418
then
419
sequence.compile_expressions_state.value =
420
DUMMY(_innate_symbol_lookup.get_array_type(Semantic.Types.ERROR()), sequence.location)
421
422
return
423
fi
424
425
type = Semantic.TUPLE_ELEMENT_LUB(_innate_symbol_lookup).combine(tuple_types, lub.element_names)
426
427
let use retry_site = RETRY_SITE_STATS.enter("tuples.sequence_element_rewalk", RetrySiteKind.REWALK_WITH_INFORMATION)
428
// going to walk the elements again, so roll back
429
// and re-speculate to avoid duplicate / misleading
430
// error messages
431
_logger.roll_back()
432
_logger.speculate()
433
434
// re-walk the type expression if present, to avoid
435
// hiding any error message it may generate
436
437
sequence.type_expression.walk(_visitor)
438
439
for e in sequence.elements do
440
e.set_expected_type(type, "element type {{0}} not compatible with inferred list type {{1}}")
441
_visitor.rewalk(e)
442
od
443
elif seen_empty_literal /\ type? /\ isa Semantic.Types.ARRAY(type) then
444
let use retry_site = RETRY_SITE_STATS.enter("tuples.sequence_empty_element_rewalk", RetrySiteKind.REWALK_WITH_INFORMATION)
445
446
_logger.roll_back()
447
_logger.speculate()
448
449
sequence.type_expression.walk(_visitor)
450
451
for e in sequence.elements do
452
e.set_expected_type(type, "element type {{0}} not compatible with inferred list type {{1}}")
453
_visitor.rewalk(e)
454
od
455
fi
456
fi
457
458
// Reconcile bottom-up LUB with any constraint pushed
459
// by the parent context. Prefer the more-specific
460
// bottom-up type when it satisfies the constraint —
461
// that preserves the runtime allocation precision the
462
// covariance idiom relies on (e.g. `let ao: object[]
463
// = ["a", "b"];` should produce a string[] at run
464
// time, even though the variable is declared
465
// object[]). When the LUB doesn't satisfy the
466
// constraint, fall back to the constraint's element
467
// type and drive per-element mismatch errors via the
468
// post-walk assignability check.
469
if constraint_type? then
470
if !type? \/ !constraint_type.is_assignable_from(type) then
471
type = constraint_type
472
have_explicit_type = true
473
fi
474
fi
475
476
if !type? then
477
if seen_error then
478
sequence.compile_expressions_state.value = DUMMY(_innate_symbol_lookup.get_array_type(Semantic.Types.ERROR()), sequence.location)
479
return
480
elif seen_any then
481
_logger.hint(
482
sequence.location,
483
"infer-object-from-list-literal",
484
"list literal element type inferred as object",
485
sequence.location,
486
"help: give the literal an explicit type")
487
type = _innate_symbol_lookup.get_object_type()
488
else
489
_logger.error(sequence.location, "cannot infer type of list literal with only null elements")
490
491
sequence.compile_expressions_state.value = DUMMY(_innate_symbol_lookup.get_array_type(Semantic.Types.ERROR()), sequence.location)
492
return
493
fi
494
fi
495
fi
496
497
let values = Collections.LIST[Value](sequence.elements.expressions.count)
498
499
for v in sequence.elements do
500
if let v.value? /\ value.type? then
501
let u = value.type
502
503
// An element whose own type is still being inferred - a
504
// local the body types from its uses - is one of those
505
// uses, so the element type the literal settled on is
506
// pushed back to it as a bound. The propagation recurses
507
// into generic arguments, so an element with a
508
// placeholder anywhere inside it is one to push into.
509
if u.contains_inferred then
510
_match_propagator.propagate_match(type, u)
511
512
values.add(v.value!)
513
514
continue
515
fi
516
517
// FIXME should push explicit type into element as constraint and retry
518
if have_explicit_type /\ !type.is_assignable_from(u) then
519
_logger.error(v.location, "element not compatible with explicit type")
520
fi
521
522
values.add(v.value!)
523
else
524
values.add(DUMMY(Semantic.Types.ERROR(), v.location))
525
fi
526
od
527
528
sequence.compile_expressions_state.value =
529
SEQUENCE(_innate_symbol_lookup.get_array_type(type), type, values)
530
si
531
si
532
si