Skip to content
← Back

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

1
namespace Syntax.Process is
2
use Logging
3
4
use Semantic.Types.Type
5
use Semantic.LEAST_UPPER_BOUND_MAP
6
use Semantic.ARM_NULL_JOIN
7
use Semantic.ArmNullJoinDecision
8
9
use IR.Values
10
11
use Ghul.Pipes
12
13
// Compiles `if` statements / expressions and their flow-sensitive
14
// narrowing. Split out of COMPILE_EXPRESSIONS, which delegates the
15
// matching pre / visit methods here. The `super.pre` / `super.visit`
16
// base-visitor calls stay in the visitor's thin stubs; the methods
17
// here are the enclosed logic, free of the visitor hierarchy.
18
//
19
// Each IF opens an IF_FLOW_FRAME on the shared flow stack;
20
// pre_if_branch does a controlled walk of each branch (condition
21
// under the running-else environment, body under the then-
22
// environment) and records the branch's exit environment;
23
// visit_if joins them into the after-IF environment.
24
class COMPILE_CONDITIONALS is
25
_logger: Logger
26
_innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup
27
_flow: NARROWING_FLOW
28
_condition_analyzer: CONDITION_ANALYZER
29
_if_flow_stack: Collections.LIST[IF_FLOW_FRAME]
30
_visitor: COMPILE_EXPRESSIONS
31
_build_flags: Compiler.GLOBAL_BUILD_FLAGS
32
_variable_left_state: VARIABLE_LEFT_STATE_STORE
33
_pattern_checker: PATTERN_CHECKER
34
_case_scrutinee_stack: Collections.LIST[Type?]
35
// Parallel to _case_scrutinee_stack: the once-spilled scrutinee
36
// load Value for the innermost case, so each `when` label's
37
// value-equality test references the single evaluation.
38
_case_scrutinee_load_stack: Collections.LIST[IR.Values.Value?]
39
// Parallel again: the scrutinee expression node itself, so a
40
// pattern arm can narrow the scrutinee variable in its body the
41
// way `if let` narrows the tested expression.
42
_case_scrutinee_expression_stack: Collections.LIST[Trees.Expressions.Expression?]
43
// Per-case flow bookkeeping, mirroring `_if_flow_stack`: each
44
// arm records its exit environment here and visit_case joins them.
45
_case_flow_stack: Collections.LIST[CASE_FLOW_FRAME]
46
_match_propagator: Semantic.MATCH_PROPAGATOR
47
_case_exhaustiveness_checker: CASE_EXHAUSTIVENESS_CHECKER
48
_arm_null_join: ARM_NULL_JOIN
49
50
init(
51
logger: Logger,
52
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup,
53
flow: NARROWING_FLOW,
54
condition_analyzer: CONDITION_ANALYZER,
55
if_flow_stack: Collections.LIST[IF_FLOW_FRAME],
56
visitor: COMPILE_EXPRESSIONS,
57
build_flags: Compiler.GLOBAL_BUILD_FLAGS,
58
variable_left_state: VARIABLE_LEFT_STATE_STORE
59
) is
60
super.init()
61
62
_logger = logger
63
_innate_symbol_lookup = innate_symbol_lookup
64
_flow = flow
65
_condition_analyzer = condition_analyzer
66
_if_flow_stack = if_flow_stack
67
_visitor = visitor
68
_build_flags = build_flags
69
_variable_left_state = variable_left_state
70
_pattern_checker = PATTERN_CHECKER(logger, build_flags, visitor, flow)
71
_case_scrutinee_stack = Collections.LIST[Type?]()
72
_case_scrutinee_load_stack = Collections.LIST[IR.Values.Value?]()
73
_case_scrutinee_expression_stack = Collections.LIST[Trees.Expressions.Expression?]()
74
_case_flow_stack = Collections.LIST[CASE_FLOW_FRAME]()
75
_match_propagator = Semantic.MATCH_PROPAGATOR(logger)
76
_case_exhaustiveness_checker = CASE_EXHAUSTIVENESS_CHECKER(logger, innate_symbol_lookup)
77
_arm_null_join = ARM_NULL_JOIN()
78
si
79
80
pre_if_branch(`if: Trees.Statements.IF_BRANCH) -> bool is
81
// Controlled walk: walk the condition, derive its
82
// then/else narrowing environments, walk the body under
83
// the then-environment, and record the body's exit
84
// environment for the join in visit(IF). The condition
85
// itself walks under the running-else environment so
86
// within-condition narrowing (`isa T(x) /\ x.foo`)
87
// applies as it compiles.
88
let frame = _if_flow_stack[_if_flow_stack.count - 1]
89
90
if `if.condition? /\ !`if.binding? then
91
let condition = `if.condition
92
let epoch = _flow.heap_epoch
93
let mark = _flow.crossing_mark
94
95
_flow.set_env(frame.running_else)
96
condition.walk(_visitor)
97
98
let facts = _condition_analyzer.analyze_condition(condition, frame.running_else)
99
100
// The branch environments derive from the snapshot
101
// taken before the condition walked. A direct store
102
// inside the condition kills heap facts the snapshot
103
// still carries — and may run after the presence
104
// check it shares an edge with — so neither
105
// carried-in nor condition-derived heap facts survive
106
// it. A call is gentler: the facts keep, carrying the
107
// call as a crossing the snapshot never saw, for the
108
// reliance judge to answer.
109
if _flow.heap_killed_since(epoch) then
110
facts.then_env.drop_heap_facts()
111
facts.else_env.drop_heap_facts()
112
else
113
_flow.adopt_crossings_since(facts.then_env, mark)
114
_flow.adopt_crossings_since(facts.else_env, mark)
115
fi
116
117
_flow.set_env(facts.then_env)
118
119
`if.body.walk(_visitor)
120
121
frame.branch_exits.add(_flow.current_env.copy())
122
frame.running_else = facts.else_env
123
elif `if.binding? then
124
// `if let` branch. Semantically equivalent to
125
// if isa V(scrutinee) /\ <bind pattern from scrutinee>
126
// when the binding has `: V`, or just a `?` presence
127
// test plus a bind from the unwrapped value for the
128
// bare form. Both shapes are handled inline off the
129
// REFUTABLE_BINDING node so that the scrutinee remains
130
// visible to flow narrowing and to the destructure's
131
// DESTRUCTURE_CONSTRAINT path — that visibility is
132
// what gives recursive-lambda inference parity with
133
// the hand-written `isa V(t) ... let p = t` shape.
134
_flow.set_env(frame.running_else)
135
136
// Snapshot the scrutinee's pre-narrow receiver type
137
// for the else-arm complement computation. The
138
// then-arm narrowing inside `check_refutable_binding`
139
// mutates the symbol's `.type` to the cast target;
140
// computing the complement against that would see a
141
// variant, not the union it belongs to.
142
// Only the first clause is a candidate for complement
143
// narrowing on the else edge — see
144
// `apply_refutable_binding_else_narrow` for the rule.
145
let binding = `if.binding!
146
let else_receiver =
147
if binding.clauses.count > 0 then
148
_resolve_clause_receiver_type(binding.clauses[0])
149
else
150
null
151
fi
152
153
let epoch = _flow.heap_epoch
154
let mark = _flow.crossing_mark
155
156
check_refutable_binding(binding)
157
158
// The else edge derives from the pre-walk snapshot,
159
// but the scrutinee (and any clause guard) has run on
160
// that edge too — a store during its walk invalidates
161
// the snapshot's heap facts, and its calls are owed
162
// as crossings. Both captured before the body walks:
163
// the body does not run on the else edge.
164
let scrutinee_killed = _flow.heap_killed_since(epoch)
165
let scrutinee_mark_end = _flow.crossing_mark
166
167
`if.body.walk(_visitor)
168
169
frame.branch_exits.add(_flow.current_env.copy())
170
171
frame.running_else =
172
_condition_analyzer.apply_refutable_binding_else_narrow(
173
binding,
174
else_receiver,
175
frame.running_else
176
)
177
178
if scrutinee_killed then
179
frame.running_else.drop_heap_facts()
180
else
181
_flow.adopt_crossings_between(frame.running_else, mark, scrutinee_mark_end)
182
fi
183
else
184
_flow.set_env(frame.running_else)
185
186
`if.body.walk(_visitor)
187
188
frame.branch_exits.add(_flow.current_env.copy())
189
frame.has_else = true
190
fi
191
192
return true
193
si
194
195
// Resolve a clause's scrutinee variable's declared (pre-narrow)
196
// type for the else-arm complement computation. Returns null
197
// when the scrutinee isn't a simple-identifier local —
198
// complement narrowing has nothing to attach to in that case.
199
_resolve_clause_receiver_type(c: Trees.Statements.REFUTABLE_BINDING_CLAUSE) -> Type? is
200
let target = _visitor.try_get_narrowing_target(c.scrutinee)
201
202
if !target? then
203
return null
204
fi
205
206
let declared = _flow.declared_type_of(target)
207
208
if !declared? then
209
return null
210
fi
211
212
if isa Semantic.Types.INFERRED_VARIABLE_TYPE(declared) then
213
let placeholder = cast Semantic.Types.INFERRED_VARIABLE_TYPE(declared)
214
let resolved = placeholder.origin.try_get_inferred_type()
215
216
if resolved? /\ !resolved.is_sentinel then
217
return resolved
218
fi
219
220
return null
221
fi
222
223
return declared
224
si
225
226
// Process a REFUTABLE_BINDING (the AST shape of an `if let`
227
// arm). For each clause, mirrors what `isa V(scrutinee) ...
228
// let p = scrutinee` would do — walk the narrow target,
229
// narrow the scrutinee on the then-edge, walk the scrutinee
230
// under the narrowed env, hand its value down to the pattern
231
// as the destructure source, walk the pattern, then the
232
// optional per-clause guard. Each clause inherits the env
233
// produced by the previous clauses, so later scrutinees see
234
// earlier bindings narrowed and in scope.
235
//
236
// The narrowing reuses the same `_apply_one` machinery the
237
// isa form uses, so the destructure walks against the same
238
// shape (INFERRED in iter 1 → DESTRUCTURE_CONSTRAINT path on
239
// the placeholder origin; concrete in later iters → normal
240
// destructure of the narrowed type) — which is what gives
241
// `if let` inference parity with `isa`.
242
check_refutable_binding(rb: Trees.Statements.REFUTABLE_BINDING) is
243
244
for c in rb.clauses do
245
_check_refutable_binding_clause(c)
246
od
247
si
248
249
_check_refutable_binding_clause(c: Trees.Statements.REFUTABLE_BINDING_CLAUSE) is
250
// 1. Resolve the narrow type (when ascribed).
251
let narrow_type: Type? mut = null
252
253
if let c.narrow_type_expression? then
254
narrow_type_expression.walk(_visitor)
255
narrow_type = narrow_type_expression.type
256
fi
257
258
// 2. Apply isa-style narrowing on the scrutinee BEFORE
259
// walking it. The narrowing mutates the scrutinee's
260
// symbol type via `_apply_one`; the subsequent load
261
// picks up the narrowed type, which propagates into
262
// the destructure source.
263
// A test against the optional of a plain value type is
264
// the exception: narrowing first would unwrap the source
265
// before the test could find it absent.
266
let lifted =
267
PATTERN_CHECKER.is_lifted_test(
268
narrow_type,
269
if let target = _visitor.try_get_narrowing_target(c.scrutinee) then _flow.declared_type_of(target) else null fi
270
)
271
272
if narrow_type? /\ !lifted then
273
let then_env =
274
_condition_analyzer.apply_refutable_binding_then_narrow(
275
c.scrutinee,
276
narrow_type,
277
_flow.current_env
278
)
279
280
_flow.set_env(then_env)
281
fi
282
283
// 3. Walk the scrutinee under the (possibly narrowed)
284
// env — produces `c.scrutinee.value`.
285
c.scrutinee.walk(_visitor)
286
287
288
// A method named without calling it is a function value,
289
// which is never absent, so there is nothing for the clause
290
// to test. Reported here rather than left to the use site,
291
// where it surfaces as a missing member on a function type,
292
// and in a generator not at all.
293
if let referenced = c.scrutinee.value?.referenced_function then
294
_logger.error(
295
c.scrutinee.location,
296
"cannot test {referenced.name} for presence: a function is never absent")
297
298
c.scrutinee.compile_expressions_state.value =
299
DUMMY(Semantic.Types.ERROR(), c.scrutinee.location)
300
fi
301
302
// Peel a flow-narrowing projection back to its wrapper —
303
// the presence test and destructure path want the
304
// optional shape, not the already-projected `T`.
305
c.scrutinee.compile_expressions_state.value = IR.Values.NARROW_PROJECT.peel(c.scrutinee.value)
306
307
// 4. Hand the scrutinee value down to the pattern as the
308
// destructure source. For an optional source the
309
// presence test in generate_il unwraps the value
310
// before the pattern destructures it.
311
let pattern = c.pattern
312
313
pattern.mark_refutable_recursive()
314
315
// For a simple-name pattern, pin the binding's static
316
// type to the narrow target — specialised against the
317
// scrutinee's receiver type so a bare variant target
318
// (`Maybe.YES`) becomes its closed-generic form
319
// (`Maybe.YES[int]`) rather than the open generic
320
// (which IL gen would emit as an unloadable class
321
// reference). Falls back to the unspecialised written
322
// form when no receiver is available.
323
//
324
// Destructure patterns leave `explicit_type` unset so
325
// pre(DESTRUCTURING_VARIABLE_LEFT)'s constraint path
326
// can fire when the scrutinee's type is still inferred —
327
// that's what gives recursive lambdas inference parity
328
// with `isa V(x) ... let (a, b) = x`.
329
let specialized_narrow: Type? mut = null
330
331
if narrow_type? then
332
specialized_narrow = narrow_type
333
334
if let receiver_type = c.scrutinee.value?.type then
335
let specialized =
336
_condition_analyzer.specialize_variant_for_receiver(
337
receiver_type,
338
narrow_type
339
)
340
341
if specialized? then
342
specialized_narrow = specialized
343
fi
344
fi
345
fi
346
347
if specialized_narrow? then
348
// The runtime test IL generation emits for this clause
349
// is against the specialized form: the written type
350
// expression names the open generic for a variant of a
351
// generic union, which is not a loadable operand. The
352
// scrutinee's own value type carries the specialization
353
// only when the then-edge narrowing had a target to
354
// mutate, so a call result would otherwise be left with
355
// the written form.
356
_variable_left_state.get_or_add(pattern).narrow_type = specialized_narrow
357
358
if pattern.is_simple_name then
359
_variable_left_state.get_or_add(pattern).explicit_type = specialized_narrow
360
fi
361
fi
362
363
let source_value: IR.Values.Value mut =
364
if c.scrutinee.value? then
365
c.scrutinee.value
366
else
367
IR.Values.DUMMY(Semantic.Types.ERROR(), c.scrutinee.location)
368
fi
369
370
// A destructure pattern reads its members off the source
371
// value's type. When the scrutinee is something the
372
// then-edge narrowing could not reach - a call result, an
373
// expression - that type is still the declared one, so
374
// view the source at the ascribed variant instead. The
375
// runtime test that makes the view sound is the isinst
376
// generate-il already emits for the clause. A source type
377
// still being inferred is left alone, so the destructure's
378
// own constraint path can drive it.
379
if let narrow = specialized_narrow /\ !pattern.is_simple_name then
380
if let source_type = source_value.type then
381
if
382
!source_type.is_inferred /\
383
!source_type.is_error /\
384
!narrow.is_assignable_from(source_type)
385
then
386
source_value = IR.Values.TYPE_WRAPPER(narrow, source_value)
387
fi
388
fi
389
fi
390
391
let pattern_state = _variable_left_state.get_or_add(pattern)
392
393
pattern_state.right_value = source_value
394
pattern_state.variable_location = c.location
395
pattern_state.right_location = c.scrutinee.location
396
397
// 5. Walk the pattern. Destructure operates on
398
// `source_value.type` (the scrutinee's narrowed type).
399
pattern.walk(_visitor)
400
401
// 5b. Register the bound names on the state-machine frame
402
// when inside a generator / async function, so their
403
// loads and stores route through frame fields rather
404
// than CLR-local slots MoveNext never declares.
405
_visitor.declare_state_machine_local_fields(pattern)
406
407
// 6. Diagnostics: redundancy / impossibility warnings
408
// (`narrowing-always-succeeds`, value-type-narrow
409
// error, etc.). Use the scrutinee variable's
410
// DECLARED type (pre-narrow) as the source — passing
411
// the already-narrowed `scrutinee.value.type` here
412
// would make every narrow look like a no-op and
413
// spuriously fire the redundancy warning.
414
let source_type: Type? mut = null
415
let target_variable = _visitor.try_get_narrowing_target(c.scrutinee)
416
417
if target_variable? then
418
source_type = _flow.declared_type_of(target_variable)
419
elif let c.scrutinee.value? then
420
source_type = value.type
421
fi
422
423
// The bare form (no top-level `: T`) needs the scrutinee
424
// itself to be refutable — UNLESS the pattern already
425
// carries its own refutability somewhere in its shape (a
426
// literal leaf, or a nested ascription): `if let (1, y) =
427
// pair` over a plain `(int, string)` can fail to match on
428
// the `1`, so it does not also need `pair` to be optional.
429
_pattern_checker.check_pattern(
430
pattern,
431
source_type,
432
narrow_type,
433
c.location,
434
!pattern.has_intrinsic_refutability
435
)
436
437
// 7. Per-clause guard — walked after the clause's names
438
// are in scope, under the then-arm env. Narrowing
439
// inside the guard applies to the rest of the chain
440
// and the then-arm.
441
if c.guard? then
442
let guard = c.guard
443
let epoch = _flow.heap_epoch
444
let mark = _flow.crossing_mark
445
446
guard.walk(_visitor)
447
448
let facts = _condition_analyzer.analyze_condition(guard, _flow.current_env)
449
450
// Guard-derived heap facts die when the guard's own
451
// walk stored — the store may run after the check it
452
// shares the edge with; its calls attach as
453
// crossings instead.
454
if _flow.heap_killed_since(epoch) then
455
facts.then_env.drop_heap_facts()
456
else
457
_flow.adopt_crossings_since(facts.then_env, mark)
458
fi
459
460
_flow.set_env(facts.then_env)
461
fi
462
si
463
464
visit_if_branch(`if: Trees.Statements.IF_BRANCH) is
465
466
if let `if.condition? /\ Value.check_is_consumable(_logger, condition.location, condition.value) then
467
// check_is_consumable is true only for a present value
468
if !condition.value!.type!.matches(_innate_symbol_lookup.get_bool_type()) then
469
_logger.error(condition.location, "if condition must be bool")
470
fi
471
fi
472
si
473
474
pre_if(`if: Trees.Statements.IF) -> bool is
475
if `if.want_value then
476
for i in `if.branches do
477
i.body.compile_expressions_state.want_value = true
478
i.body.compile_expressions_state.void_tolerated = `if.void_tolerated
479
od
480
fi
481
482
// Open a flow frame for this IF. Each branch's controlled
483
// walk (pre(IF_BRANCH)) records its exit environment on
484
// the frame; visit(IF) joins them.
485
_if_flow_stack.add(IF_FLOW_FRAME(_flow.current_env.copy()))
486
487
return false
488
si
489
490
visit_if(`if: Trees.Statements.IF) is
491
// Cleared before anything else runs, so early-return paths
492
// below leave it false rather than inheriting a previous
493
// walk's value.
494
`if.yields_absence_on_fall_through = false
495
496
// Merge the branch exit environments into the environment
497
// in force after the IF. A branch whose body diverges
498
// contributes the bottom environment; with no else
499
// branch, the no-branch-taken fall-through contributes
500
// the trailing running-else environment.
501
let if_flow = _if_flow_stack[_if_flow_stack.count - 1]
502
_if_flow_stack.remove_at(_if_flow_stack.count - 1)
503
504
let after mut = NARROW_ENV.bottom()
505
506
for branch_exit in if_flow.branch_exits do
507
after = NARROW_ENV.join(after, branch_exit)
508
od
509
510
if !if_flow.has_else then
511
after = NARROW_ENV.join(after, if_flow.running_else)
512
fi
513
514
_flow.set_env(after)
515
516
for i in `if.branches do
517
if let i.condition? then
518
IR.Values.Value.check_is_consumable(_logger, condition.location, condition.value)
519
fi
520
od
521
522
if !`if.want_value then
523
return
524
fi
525
526
if `if.is_poisoned then
527
// if the if is syntactically incomplete don't bother reporting any
528
// semantic errors:
529
`if.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `if.location, "if is poisoned")
530
531
return
532
fi
533
534
let expected_type = `if.expected_type
535
536
if
537
VOID_TOLERANT_POSITION.is_undecided(
538
`if.void_tolerated,
539
`if.branches |> any(b => VOID_TOLERANT_POSITION.counts_as_non_void(b.body.value?.type)),
540
`if.branches |> any(b => VOID_TOLERANT_POSITION.is_in_error(b.body.value?.type))
541
)
542
then
543
`if.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `if.location, "an arm is in error")
544
545
return
546
fi
547
548
if
549
VOID_TOLERANT_POSITION.declines_value(
550
`if.void_tolerated,
551
`if.branches |> any(b => VOID_TOLERANT_POSITION.counts_as_non_void(b.body.value?.type))
552
)
553
then
554
`if.compile_expressions_state.want_value = false
555
556
for i in `if.branches do
557
_drop_arm_value_request(i.body)
558
od
559
560
return
561
fi
562
563
let missing_else =
564
!(`if.branches |> any(b => !b.condition? /\ !b.binding?)) /\
565
(!expected_type? \/ !expected_type.is_void)
566
567
let type mut = expected_type
568
569
let seen_any_values mut = false
570
let seen_non_null_values mut = false
571
let seen_null_values mut = false
572
// A genuine null literal, as opposed to an unsettled inferred
573
// sentinel or an error type (both of which also answer is_null).
574
// Only a genuine null arm justifies widening a value-type LUB
575
// to its optional carrier; a sentinel arm during inference
576
// must leave the LUB untouched so it can still converge.
577
let seen_genuine_null_values mut = false
578
let all_tuple_literals mut = true
579
580
let lub = LEAST_UPPER_BOUND_MAP()
581
582
let arm_join = Semantic.EXPECTED_ARM_JOIN(expected_type)
583
584
let empty_literal_arms = Collections.LIST[Trees.Statements.LIST]()
585
586
for i in `if.branches do
587
let branch = i.body
588
589
let value = branch.value
590
591
if !value? then
592
continue
593
fi
594
595
if !value.type? then
596
arm_join.add_untyped_arm()
597
continue
598
fi
599
600
if expected_type? then
601
if !value.check_is_consumable_allow_void(_logger, branch.location) then
602
arm_join.add_untyped_arm()
603
continue
604
fi
605
else
606
if !value.check_is_consumable(_logger, branch.location) then
607
continue
608
fi
609
fi
610
611
debug_indent()
612
613
if expected_type? then
614
if !expected_type.is_void /\ !expected_type.is_assignable_from(value.type!) then
615
// set_expected_type assigns both fields together — caller pairs them
616
_logger.error(branch.location, string.format(`if.expected_type_error_message!, value.type, type))
617
fi
618
619
// Assignability alone does not propagate the branch
620
// type into phantom slots of the pushed-down
621
// constraint; without this match propagation an outer
622
// generic call's type-arg slot stays unresolved and
623
// is reported as cannot infer type here even though
624
// the branch supplied the concrete type.
625
_match_propagator.propagate_match(expected_type, value.type!)
626
627
arm_join.add(value.type!)
628
elif CONTEXTLESS_EMPTY_LITERAL.is_arm(branch) then
629
empty_literal_arms.add(branch)
630
elif value.type!.is_null then
631
// could also be error
632
seen_null_values = true
633
634
if _arm_null_join.is_genuine_null(value.type!) then
635
seen_genuine_null_values = true
636
fi
637
else
638
seen_non_null_values = true
639
640
if !branch.is_tuple_literal /\ !((value.type!.is_value_tuple \/ isa Semantic.Types.TUPLE(value.type)) /\ !value.type!.is_optional) then
641
all_tuple_literals = false
642
fi
643
644
lub.add(value.type!)
645
fi
646
647
seen_any_values = true
648
od
649
650
if _settle_empty_literal_arms(empty_literal_arms, lub, seen_non_null_values) then
651
seen_non_null_values = true
652
all_tuple_literals = false
653
fi
654
655
// A missing unconditional else makes the construct refutable:
656
// it yields an optional of its completing arms' LUB, absent
657
// when no arm runs - the same answer a loop expression gives
658
// exhaustion. An if whose every arm diverges delivers nothing
659
// and cannot self-type, and a non-optional expected type has
660
// no slot for absence, so both keep demanding an else.
661
let yields_absence mut = false
662
663
if missing_else then
664
if IF_MISSING_ELSE_DECIDER().decide(expected_type, seen_any_values) == IfMissingElseDecision.REQUIRE_ELSE then
665
_logger.error(`if.location, "expected else in if expression")
666
`if.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `if.location, "no else")
667
return
668
fi
669
670
yields_absence = true
671
672
// The fall-through path contributes absence: genuinely
673
// null, so the LUB below widens to the optional carrier.
674
seen_genuine_null_values = true
675
seen_null_values = true
676
fi
677
678
`if.yields_absence_on_fall_through = yields_absence
679
680
if !expected_type? then
681
type = lub.get_result()
682
683
if all_tuple_literals /\ seen_non_null_values /\ (!type? \/ !type.is_value_tuple) then
684
let tuple_types = Collections.LIST[Type]()
685
686
for i in `if.branches do
687
let branch = i.body
688
689
if let branch.value? /\ value.type? then
690
tuple_types.add(value.type)
691
fi
692
od
693
694
type = Semantic.TUPLE_ELEMENT_LUB(_innate_symbol_lookup).combine(tuple_types, lub.element_names)
695
fi
696
697
// An arm still holding placeholders - a function literal
698
// whose parameter only a member access constrains - is
699
// represented in the joined type by the other arms, and
700
// learns from them what a use of the whole expression
701
// pushes into that type. An arm that is itself a
702
// placeholder - a local variable whose type its own
703
// assignments decide - takes nothing from the other arms:
704
// their join is no bound on what it can hold.
705
if type? then
706
for i in `if.branches do
707
if let arm_value = i.body.value, arm_type = arm_value.type /\ arm_type.contains_inferred /\ !arm_type.is_inferred then
708
_match_propagator.propagate_match(type, arm_type)
709
fi
710
od
711
fi
712
713
let decision = _arm_null_join.decide(type, seen_genuine_null_values, seen_null_values)
714
715
if decision == ArmNullJoinDecision.WIDEN_VALUE_OPTIONAL then
716
type = _innate_symbol_lookup.get_optional_type(type!)
717
elif decision == ArmNullJoinDecision.WIDEN_REFERENCE_OPTIONAL then
718
type = type!.as_optional()
719
elif decision == ArmNullJoinDecision.INCOMPATIBLE then
720
// A value-type or reference-type LUB against a non-
721
// genuine null arm: an unsettled inferred sentinel
722
// (resolves later, so the error is discarded on
723
// speculative passes) or a permanently error-typed
724
// branch (which surfaces here).
725
for i in `if.branches do
726
let branch = i.body
727
728
let value = branch.value
729
730
if !value? \/ !value.type? then
731
continue
732
fi
733
734
if value.type.is_null then
735
_logger.error(branch.location, "incompatible types in if branches: {value.type} and {type}")
736
fi
737
od
738
fi
739
fi
740
741
if let joined = arm_join.result(yields_absence) then
742
type = joined
743
fi
744
745
if !type? then
746
if seen_any_values then
747
if seen_non_null_values then
748
_logger.error(`if.location, "no type inferred for if expression")
749
else
750
_logger.error(`if.location, "all branch values are null")
751
fi
752
fi
753
754
`if.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `if.location, "no type inferred")
755
return
756
else
757
`if.compile_expressions_state.value = IR.Values.BLOCK(type)
758
fi
759
si
760
761
// Arms that are an empty list literal with nothing to say what
762
// it holds have stayed out of the join. Where the other arms
763
// join to an array, each is walked again at that type; otherwise
764
// they join as they are. Answers whether any arm was added.
765
_settle_empty_literal_arms(
766
arms: Collections.List[Trees.Statements.LIST],
767
lub: LEAST_UPPER_BOUND_MAP,
768
seen_sibling: bool
769
) -> bool is
770
if arms.count == 0 then
771
return false
772
fi
773
774
let sibling_type: Type? = if seen_sibling then lub.get_result() else null fi
775
776
if sibling_type? /\ isa Semantic.Types.ARRAY(sibling_type) /\ !sibling_type.contains_inferred then
777
let use retry_site = RETRY_SITE_STATS.enter("conditionals.empty_literal_arm_rewalk", RetrySiteKind.REWALK_WITH_INFORMATION)
778
779
for arm in arms do
780
arm.set_expected_type(sibling_type, "arm type {{0}} not compatible with type {{1}} of the other arms")
781
_visitor.rewalk(arm)
782
od
783
fi
784
785
for arm in arms do
786
if let arm.value? /\ value.type? then
787
lub.add(value.type)
788
fi
789
od
790
791
return true
792
si
793
794
// Undo the value request an arm was walked under, once the
795
// composite has decided it produces no value after all. The
796
// arm's captured BLOCK would otherwise swallow the arm's IL:
797
// nothing consumes it, so nothing ever emits it. Clearing it
798
// leaves the arm emitting its own statements, which is the
799
// shape a body whose return type was written out as void has
800
// been compiled to all along.
801
_drop_arm_value_request(arm: Trees.Statements.LIST) is
802
arm.compile_expressions_state.want_value = false
803
arm.compile_expressions_state.value = null
804
805
if let arm.last? then
806
last.compile_expressions_state.want_value = false
807
808
if let tail: Trees.Statements.EXPRESSION = last then
809
if let statement_expression: Trees.Expressions.STATEMENT = tail.expression then
810
statement_expression.want_value = false
811
elif let val_block: Trees.Expressions.VAL_BLOCK = tail.expression then
812
val_block.want_value = false
813
fi
814
elif
815
isa Trees.Statements.FOR(last) \/
816
isa Trees.Statements.DO(last)
817
then
818
// A loop walked under the demand captured a value
819
// block for its break convergence; with the request
820
// dropped that block has no consumer, and IL
821
// generation's spiller would capture the loop's
822
// body into it - silently losing the loop. Drop the
823
// value so the loop emits as the statement it now
824
// is.
825
last.compile_expressions_state.value = null
826
fi
827
fi
828
si
829
830
pre_case(`case: Trees.Statements.CASE) -> bool is
831
if `case.want_value then
832
for m in `case.matches do
833
m.statements.compile_expressions_state.want_value = true
834
m.statements.compile_expressions_state.void_tolerated = `case.void_tolerated
835
od
836
fi
837
838
// Controlled walk: walk the scrutinee first so its value
839
// is available to spill, then walk every match. The
840
// scrutinee's type is held on a stack so nested `case`
841
// statements still find their own scrutinee at the top.
842
`case.expression.walk(_visitor)
843
844
let scrutinee_value = `case.expression.value
845
846
// Spill the scrutinee once into a temp, so every `when`
847
// label's value-equality test (built in pre_case_match)
848
// and every pattern arm reads the single evaluation -
849
// matching the single-evaluation semantics the case
850
// scrutinee has always had. `get_temp_copier` is a no-op
851
// spill for an already-repeatable load (a local/parameter).
852
let spill_block = IR.Values.BLOCK(scrutinee_value?.type ?? Semantic.Types.ERROR())
853
let scrutinee_load =
854
if scrutinee_value? then
855
scrutinee_value.get_temp_copier(spill_block, "case")()
856
else
857
scrutinee_value
858
fi
859
860
`case.case_state.set_scrutinee(spill_block, scrutinee_load)
861
862
let scrutinee_type = scrutinee_value?.type
863
864
_case_scrutinee_stack.add(scrutinee_type)
865
_case_scrutinee_load_stack.add(scrutinee_load)
866
_case_scrutinee_expression_stack.add(`case.expression)
867
868
// Every arm walks from this same entry environment — the one
869
// in force once the scrutinee has been walked and spilled.
870
_case_flow_stack.add(CASE_FLOW_FRAME(_flow.current_env.copy()))
871
872
for m in `case.matches do
873
m.walk(_visitor)
874
od
875
876
_case_scrutinee_stack.remove_at(_case_scrutinee_stack.count - 1)
877
_case_scrutinee_load_stack.remove_at(_case_scrutinee_load_stack.count - 1)
878
_case_scrutinee_expression_stack.remove_at(_case_scrutinee_expression_stack.count - 1)
879
880
// The arm exits are joined in `visit_case`, which runs next:
881
// the join has to know whether the case is exhaustive, the
882
// checker settles that, and the checker in turn has to run
883
// after the void-tolerant decision there. Nothing between
884
// here and there reads the flow environment.
885
886
return true
887
si
888
889
pre_case_match(arm: Trees.Statements.CASE_MATCH) -> bool is
890
let pattern = arm.pattern
891
892
// Expression-list arm (`when expr, ...`): walk each label,
893
// build its value-equality test against the once-spilled
894
// scrutinee (the way `=~`, falling back to `<>`, would),
895
// and park the results on the arm for generate-il to branch
896
// on. A null test means no operator resolved (or the label
897
// is `null`); generate-il then falls back to a raw compare
898
// or a presence test. Doing this here keeps the operator
899
// resolution - and any overload-resolution failure - in the
900
// compile-expressions pass.
901
if !pattern? then
902
// An expression-list arm narrows nothing, but its body
903
// still walks from the shared entry environment and still
904
// has to contribute its exit to the join — a kill inside
905
// it (an assignment, a heap-mutating call) must survive
906
// the `case` just as it would from a pattern arm. A null
907
// `expressions` marks the `else` arm, which covers the
908
// no-arm-matched path.
909
_reset_to_case_entry()
910
911
if arm.expressions? then
912
let scrutinee_load =
913
if _case_scrutinee_load_stack.count > 0 then
914
_case_scrutinee_load_stack[_case_scrutinee_load_stack.count - 1]
915
else
916
null
917
fi
918
919
let tests = Collections.LIST[IR.Values.Value?]()
920
921
for e in arm.expressions.expressions do
922
// A label names a value of the scrutinee's
923
// type, which is what settles the type
924
// arguments of a bare generic variant.
925
if _case_scrutinee_stack.count > 0 then
926
let scrutinee_type = _case_scrutinee_stack[_case_scrutinee_stack.count - 1]
927
928
if scrutinee_type? /\ scrutinee_type.is_settled then
929
e.set_expected_type(scrutinee_type, "")
930
fi
931
fi
932
933
e.walk(_visitor)
934
935
let test: IR.Values.Value? mut = null
936
let label_value = e.value
937
938
// A `null` label is a presence test, not a value
939
// comparison: skip operator resolution (a null
940
// operand would build a null-typed temp the back
941
// end cannot encode), leaving a null test for
942
// generate-il to handle as match-absence.
943
if label_value? /\ scrutinee_load? /\ !(label_value.type?.is_null ?? false) then
944
test =
945
_visitor.build_equality_test(scrutinee_load, label_value, "=~", arm.location) ??
946
_visitor.build_equality_test(scrutinee_load, label_value, "<>", arm.location)
947
fi
948
949
tests.add(test)
950
od
951
952
arm.match_state.tests = tests
953
fi
954
955
arm.statements.walk(_visitor)
956
957
_record_case_arm_exit(!arm.expressions?)
958
959
return true
960
fi
961
962
let scrutinee_type: Type? mut = null
963
if _case_scrutinee_stack.count > 0 then
964
scrutinee_type = _case_scrutinee_stack[_case_scrutinee_stack.count - 1]
965
fi
966
967
// Controlled walk of the pattern. The standard
968
// `pre(VARIABLE)` flow expects an `=` initializer to thread
969
// through into `left.right_value`; case arms don't have
970
// one, so we synthesise the pieces by hand: resolve the
971
// type ascription, push the explicit type + refutability
972
// down to the left, build a stand-in right_value of the
973
// scrutinee type (wrapped in a CAST for the ascribed form
974
// so the bound symbols are typed at the narrowed type, not
975
// the scrutinee's), then walk the left to fire the
976
// per-shape symbol-typing in `visit(SIMPLE_VARIABLE_LEFT)`
977
// / `pre(DESTRUCTURING_VARIABLE_LEFT)`.
978
pattern.type_expression.walk(_visitor)
979
980
let left_state = _variable_left_state.get_or_add(pattern.left)
981
982
let target_type: Type? mut = null
983
if pattern.is_explicit_type /\ pattern.type_expression.type? then
984
// A variant names the union's generic parameters, so
985
// an arm written `when v: OK` over a scrutinee of
986
// `Result[double, string]` resolves to the
987
// unconstructed `OK`. Bind the union's arguments onto
988
// it, so the arm tests and binds at
989
// `OK[double, string]`. Written back onto the type
990
// expression because the emitted isinst, the arm's
991
// locals and the field-access owner spec all read it,
992
// and an unconstructed variant names no loadable type.
993
let pattern_type_expression = pattern.type_expression
994
995
target_type =
996
_condition_analyzer.specialize_variant_for_receiver(
997
scrutinee_type,
998
pattern_type_expression.type
999
)
1000
1001
pattern_type_expression.type = target_type
1002
left_state.explicit_type = target_type!
1003
fi
1004
1005
pattern.left.mark_refutable_recursive()
1006
1007
if scrutinee_type? then
1008
let synthesized: IR.Values.Value mut =
1009
IR.Values.DUMMY(scrutinee_type, arm.location)
1010
1011
if target_type? then
1012
synthesized = IR.Values.CAST(target_type, synthesized, false)
1013
fi
1014
1015
left_state.right_value = synthesized
1016
left_state.right_location = arm.location
1017
left_state.variable_location = arm.location
1018
fi
1019
1020
pattern.left.walk(_visitor)
1021
1022
// Same registration the `if let` clause path does: a
1023
// case-when pattern's bound names are body locals, so
1024
// inside a generator they need state-machine frame fields.
1025
_visitor.declare_state_machine_local_fields(pattern.left)
1026
1027
// `case`-when patterns do not require implicit refutability
1028
// on the bare form — a destructure of a non-nullable tuple
1029
// is a valid arm that simply always matches, not an error
1030
// the way `if let v = some_int` is.
1031
_pattern_checker.check_pattern(
1032
pattern.left,
1033
scrutinee_type,
1034
target_type,
1035
arm.location,
1036
false
1037
)
1038
1039
// Arms are alternatives, so each one walks from the shared
1040
// pre-case entry environment rather than from whatever the
1041
// previous arm left behind.
1042
let epoch = _flow.heap_epoch
1043
let mark = _flow.crossing_mark
1044
1045
_reset_to_case_entry()
1046
1047
// An ascribed pattern is a runtime type test on the
1048
// scrutinee, so the scrutinee itself reads at the tested
1049
// type inside the arm, exactly as `if let v: T = e` narrows
1050
// `e`. The scrutinee was walked and spilled once before any
1051
// arm ran and no arm has run yet, so the fact holds on entry
1052
// to every arm.
1053
if
1054
target_type? /\ !PATTERN_CHECKER.is_lifted_test(target_type, scrutinee_type) /\
1055
_case_scrutinee_expression_stack.count > 0
1056
then
1057
let scrutinee_expression =
1058
_case_scrutinee_expression_stack[_case_scrutinee_expression_stack.count - 1]
1059
1060
if scrutinee_expression? then
1061
_flow.set_env(
1062
_condition_analyzer.apply_refutable_binding_then_narrow(
1063
scrutinee_expression,
1064
target_type,
1065
_flow.current_env
1066
)
1067
)
1068
fi
1069
fi
1070
1071
// Per-arm guard — walked after the pattern's names are in
1072
// scope, so identifiers the pattern bound resolve inside it.
1073
// Run the guard through the condition analyzer and apply its
1074
// then-environment to the arm body, mirroring an `if let`
1075
// guard (see _check_refutable_binding_clause): a presence or
1076
// `isa` test in the guard narrows within the body.
1077
if let guard = arm.guard then
1078
guard.walk(_visitor)
1079
1080
let facts = _condition_analyzer.analyze_condition(guard, _flow.current_env)
1081
1082
if _flow.heap_killed_since(epoch) then
1083
facts.then_env.drop_heap_facts()
1084
else
1085
_flow.adopt_crossings_since(facts.then_env, mark)
1086
fi
1087
1088
_flow.set_env(facts.then_env)
1089
fi
1090
1091
arm.statements.walk(_visitor)
1092
1093
_record_case_arm_exit(false)
1094
1095
return true
1096
si
1097
1098
visit_case_match(arm: Trees.Statements.CASE_MATCH) is
1099
si
1100
1101
// Reset to the environment every arm starts from. Arms are
1102
// alternatives, so no arm inherits what a previous arm narrowed
1103
// or killed.
1104
_reset_to_case_entry() is
1105
if _case_flow_stack.count > 0 then
1106
_flow.set_env(_case_flow_stack[_case_flow_stack.count - 1].entry.copy())
1107
fi
1108
si
1109
1110
// Record the environment an arm body exits with, so visit_case
1111
// can join it with the other arms'. A body that diverges exits
1112
// at the bottom environment and contributes nothing to the join.
1113
_record_case_arm_exit(is_else: bool) is
1114
if _case_flow_stack.count > 0 then
1115
let frame = _case_flow_stack[_case_flow_stack.count - 1]
1116
1117
frame.branch_exits.add(_flow.current_env.copy())
1118
1119
if is_else then
1120
frame.has_else = true
1121
fi
1122
fi
1123
si
1124
1125
// Join the arm exits into the environment in force after the
1126
// `case`. Without an `else` arm the no-arm-matched path falls
1127
// through carrying the entry environment, so that joins too —
1128
// and because the join is a meet over facts, anything an arm
1129
// killed is correctly absent from the result. An exhaustive
1130
// case has no such path: one arm always matches, so joining the
1131
// entry there would make the code after look reachable when
1132
// every arm diverged, and a body ending in one would draw a
1133
// spurious definite-return.
1134
_join_case_arm_exits(is_exhaustive: bool) is
1135
if _case_flow_stack.count == 0 then
1136
return
1137
fi
1138
1139
let frame = _case_flow_stack[_case_flow_stack.count - 1]
1140
_case_flow_stack.remove_at(_case_flow_stack.count - 1)
1141
1142
let after mut = NARROW_ENV.bottom()
1143
1144
for branch_exit in frame.branch_exits do
1145
after = NARROW_ENV.join(after, branch_exit)
1146
od
1147
1148
if !frame.has_else /\ !is_exhaustive then
1149
after = NARROW_ENV.join(after, frame.entry)
1150
fi
1151
1152
_flow.set_env(after)
1153
si
1154
1155
visit_case(`case: Trees.Statements.CASE) is
1156
let undecided =
1157
VOID_TOLERANT_POSITION.is_undecided(
1158
`case.void_tolerated,
1159
`case.matches |> any(m => VOID_TOLERANT_POSITION.counts_as_non_void(m.statements.value?.type)),
1160
`case.matches |> any(m => VOID_TOLERANT_POSITION.is_in_error(m.statements.value?.type))
1161
)
1162
1163
// Decided before the exhaustiveness check, so a case whose
1164
// arms only yield void is checked as the statement it is:
1165
// a missing `else` there is the statement form's warning,
1166
// not the expression form's error.
1167
if
1168
!undecided /\
1169
VOID_TOLERANT_POSITION.declines_value(
1170
`case.void_tolerated,
1171
`case.matches |> any(m => VOID_TOLERANT_POSITION.counts_as_non_void(m.statements.value?.type))
1172
)
1173
then
1174
`case.compile_expressions_state.want_value = false
1175
1176
for m in `case.matches do
1177
_drop_arm_value_request(m.statements)
1178
od
1179
fi
1180
1181
if !`case.is_poisoned then
1182
_case_exhaustiveness_checker.check(`case)
1183
fi
1184
1185
// Deferred from `pre_case`, and placed after the check so it
1186
// knows whether the case is exhaustive: one that is has no
1187
// fall-through path for the join to add.
1188
_join_case_arm_exits(`case.is_exhaustive)
1189
1190
if !`case.want_value then
1191
return
1192
fi
1193
1194
if `case.is_poisoned then
1195
`case.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `case.location, "case is poisoned")
1196
return
1197
fi
1198
1199
if undecided then
1200
`case.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `case.location, "an arm is in error")
1201
1202
return
1203
fi
1204
1205
let has_else mut = false
1206
for m in `case.matches do
1207
if !m.expressions? then
1208
has_else = true
1209
fi
1210
od
1211
1212
// Whether the case is guaranteed to produce a value when
1213
// none of the arms match:
1214
// - has_else → else arm runs
1215
// - is_exhaustive → checker proved one arm matches
1216
// - requires_default_fallthrough → IL pushes default(expected_type)
1217
// Otherwise no value can be produced. CASE_EXHAUSTIVENESS_CHECKER
1218
// has already emitted the relevant diagnostic
1219
// (`non-exhaustive-case` for a closed domain with gaps,
1220
// `case-needs-else` for an open domain with no else).
1221
let expected_type = `case.expected_type
1222
1223
if
1224
!has_else /\
1225
!`case.is_exhaustive /\
1226
!`case.requires_default_fallthrough /\
1227
(!expected_type? \/ !expected_type.is_void)
1228
then
1229
`case.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `case.location, "no else")
1230
return
1231
fi
1232
1233
let type mut = expected_type
1234
1235
let seen_any_values mut = false
1236
let seen_non_null_values mut = false
1237
let seen_null_values mut = false
1238
// See the IF path: only a genuine settled null arm justifies
1239
// widening a value-type LUB, not an unsettled inferred sentinel.
1240
let seen_genuine_null_values mut = false
1241
1242
let lub = LEAST_UPPER_BOUND_MAP()
1243
1244
let empty_literal_arms = Collections.LIST[Trees.Statements.LIST]()
1245
1246
for m in `case.matches do
1247
let body = m.statements
1248
1249
let value = body.value
1250
1251
if !value? then
1252
continue
1253
fi
1254
1255
if !value.type? then
1256
continue
1257
fi
1258
1259
if expected_type? then
1260
if !value.check_is_consumable_allow_void(_logger, body.location) then
1261
continue
1262
fi
1263
else
1264
if !value.check_is_consumable(_logger, body.location) then
1265
continue
1266
fi
1267
fi
1268
1269
if expected_type? then
1270
if !expected_type.is_void /\ !expected_type.is_assignable_from(value.type!) then
1271
// set_expected_type assigns both fields together — caller pairs them
1272
_logger.error(body.location, string.format(`case.expected_type_error_message!, value.type, type))
1273
fi
1274
elif CONTEXTLESS_EMPTY_LITERAL.is_arm(body) then
1275
empty_literal_arms.add(body)
1276
elif value.type!.is_null then
1277
seen_null_values = true
1278
1279
if _arm_null_join.is_genuine_null(value.type!) then
1280
seen_genuine_null_values = true
1281
fi
1282
else
1283
seen_non_null_values = true
1284
1285
lub.add(value.type!)
1286
fi
1287
1288
seen_any_values = true
1289
od
1290
1291
if _settle_empty_literal_arms(empty_literal_arms, lub, seen_non_null_values) then
1292
seen_non_null_values = true
1293
fi
1294
1295
if !expected_type? then
1296
type = lub.get_result()
1297
1298
let decision = _arm_null_join.decide(type, seen_genuine_null_values, seen_null_values)
1299
1300
if decision == ArmNullJoinDecision.WIDEN_VALUE_OPTIONAL then
1301
type = _innate_symbol_lookup.get_optional_type(type!)
1302
elif decision == ArmNullJoinDecision.WIDEN_REFERENCE_OPTIONAL then
1303
type = type!.as_optional()
1304
elif decision == ArmNullJoinDecision.INCOMPATIBLE then
1305
// A value-type or reference-type LUB against a non-
1306
// genuine null arm — see the IF path.
1307
for m in `case.matches do
1308
let body = m.statements
1309
1310
let value = body.value
1311
1312
if !value? \/ !value.type? then
1313
continue
1314
fi
1315
1316
if value.type.is_null then
1317
_logger.error(body.location, "incompatible types in case arms: {value.type} and {type}")
1318
fi
1319
od
1320
fi
1321
fi
1322
1323
if !type? then
1324
if seen_any_values then
1325
if seen_non_null_values then
1326
_logger.error(`case.location, "no type inferred for case expression")
1327
else
1328
_logger.error(`case.location, "all arm values are null")
1329
fi
1330
fi
1331
1332
`case.compile_expressions_state.value = DUMMY_BLOCK(Semantic.Types.ERROR(), `case.location, "no type inferred")
1333
return
1334
else
1335
`case.compile_expressions_state.value = IR.Values.BLOCK(type)
1336
fi
1337
si
1338
si
1339
si