vxx / vlib / v3 / transform / if.v
1323 lines · 1262 sloc · 42.44 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module transform
2
3import v3.flat
4
5// try_expand_if_guard detects an if-guard pattern where the condition is a
6// decl_assign whose RHS is a call returning an optional (?T) or result (!T).
7// When detected it expands:
8// if val := maybe_call() { body }
9// into:
10// __or_tmp_N := maybe_call()
11// if !__or_tmp_N.is_error { val := __or_tmp_N.data; body... }
12//
13fn (mut t Transformer) try_expand_if_guard(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
14 if node.kind != .if_expr || node.children_count < 2 {
15 return none
16 }
17 cond_id := t.a.child(&node, 0)
18 cond := t.a.nodes[int(cond_id)]
19 if cond.kind != .decl_assign || cond.children_count < 2 {
20 return none
21 }
22 lhs_id := t.a.child(&cond, 0)
23 rhs_id := t.a.child(&cond, 1)
24 lhs := t.a.nodes[int(lhs_id)]
25 if lhs.kind != .ident || lhs.value.len == 0 {
26 return none
27 }
28 lhs_ids := t.multi_assign_lhs_ids(cond)
29 if lhs_ids.len == 1 {
30 if info := t.map_index_info(rhs_id) {
31 return t.expand_map_index_if_guard(node, lhs.value, info)
32 }
33 if info := t.array_index_info(rhs_id) {
34 return t.expand_array_index_if_guard(node, lhs.value, info)
35 }
36 }
37 mut rhs_type := t.optional_result_expr_type_name(rhs_id)
38 if !t.is_optional_type_name(rhs_type) {
39 return none
40 }
41 rhs_type = t.qualify_optional_type(rhs_type)
42 value_type := t.optional_base_type(rhs_type)
43 tmp_name := t.new_temp('if_guard')
44 rhs_expr := t.transform_expr(rhs_id)
45 mut prelude := []flat.NodeId{}
46 t.drain_pending(mut prelude)
47 tmp_decl := t.make_decl_assign_typed(tmp_name, rhs_expr, rhs_type)
48 ok_cond := t.make_selector(t.make_ident(tmp_name), 'ok', 'bool')
49 mut value_decls := []flat.NodeId{}
50 if lhs_ids.len > 1 {
51 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
52 for i, lhs_item_id in lhs_ids {
53 lhs_item := t.a.nodes[int(lhs_item_id)]
54 if lhs_item.kind != .ident || lhs_item.value == '_' {
55 continue
56 }
57 field_type := rhs_types[i].name()
58 payload := t.make_selector(t.make_ident(tmp_name), 'value', value_type)
59 field := t.make_selector(payload, 'arg${i}', field_type)
60 value_decls << t.make_decl_assign_typed(lhs_item.value, field, field_type)
61 }
62 }
63 }
64 if value_decls.len == 0 && lhs.value != '_' && value_type != 'void' {
65 value_decls << t.make_decl_assign_typed(lhs.value, t.make_selector(t.make_ident(tmp_name),
66 'value', value_type), value_type)
67 }
68
69 then_id := t.a.child(&node, 1)
70 then_node := t.a.nodes[int(then_id)]
71 if lhs_ids.len > 1 {
72 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
73 for i, lhs_item_id in lhs_ids {
74 lhs_item := t.a.nodes[int(lhs_item_id)]
75 if lhs_item.kind == .ident && lhs_item.value.len > 0 && lhs_item.value != '_' {
76 t.set_var_type(lhs_item.value, rhs_types[i].name())
77 }
78 }
79 }
80 } else {
81 if lhs.value != '_' && value_type != 'void' {
82 t.set_var_type(lhs.value, value_type)
83 }
84 }
85 mut then_children := []flat.NodeId{}
86 for value_decl in value_decls {
87 then_children << value_decl
88 }
89 if then_node.kind == .block {
90 then_children << t.transform_stmts(t.a.children_of(&then_node))
91 } else {
92 then_children << t.transform_stmt(then_id)
93 }
94 then_block := t.make_block(then_children)
95
96 mut else_block := flat.empty_node
97 if node.children_count >= 3 {
98 else_id := t.a.child(&node, 2)
99 else_node := t.a.nodes[int(else_id)]
100 else_block = t.transform_if_guard_else_block(else_id, else_node, tmp_name)
101 }
102 mut expanded := []flat.NodeId{cap: prelude.len + 2}
103 for stmt in prelude {
104 expanded << stmt
105 }
106 expanded << tmp_decl
107 expanded << t.make_if(ok_cond, then_block, else_block)
108 return expanded
109}
110
111// optional_result_expr_type_name supports optional result expr type name handling for Transformer.
112fn (mut t Transformer) optional_result_expr_type_name(id flat.NodeId) string {
113 if int(id) < 0 {
114 return ''
115 }
116 if !isnil(t.tc) {
117 node := t.a.nodes[int(id)]
118 if node.kind == .call {
119 concrete_ret := t.concrete_generic_call_return_type(id, node)
120 if t.is_optional_type_name(concrete_ret) {
121 return concrete_ret
122 }
123 if name := t.tc.resolved_call_name(id) {
124 if ret := t.tc.fn_ret_types[name] {
125 ret_name := ret.name()
126 if t.is_optional_type_name(ret_name) {
127 return ret_name
128 }
129 }
130 }
131 ret_name := t.get_call_return_type(id, node)
132 if t.is_optional_type_name(ret_name) {
133 return ret_name
134 }
135 }
136 if typ := t.tc.expr_type(id) {
137 typ_name := typ.name()
138 if t.is_optional_type_name(typ_name) {
139 return typ_name
140 }
141 }
142 }
143 return t.node_type(id)
144}
145
146// transform_if_guard_else_block transforms transform if guard else block data for transform.
147fn (mut t Transformer) transform_if_guard_else_block(else_id flat.NodeId, else_node flat.Node, err_source string) flat.NodeId {
148 saved_var_types := t.var_types.clone()
149 t.set_var_type('err', 'IError')
150 mut children := []flat.NodeId{}
151 err_value := if err_source.len > 0 {
152 t.make_selector(t.make_ident(err_source), 'err', 'IError')
153 } else {
154 t.make_struct_init('IError')
155 }
156 children << t.make_decl_assign_typed('err', err_value, 'IError')
157 if else_node.kind == .block {
158 children << t.transform_stmts(t.a.children_of(&else_node))
159 } else if else_node.kind == .if_expr {
160 children << t.transform_else_if_expr(else_id, else_node)
161 } else {
162 children << t.transform_stmt(else_id)
163 }
164 t.var_types = saved_var_types
165 return t.make_block(children)
166}
167
168// transform_else_if_expr transforms transform else if expr data for transform.
169fn (mut t Transformer) transform_else_if_expr(else_id flat.NodeId, else_node flat.Node) flat.NodeId {
170 if expanded := t.try_expand_if_guard(else_id, else_node) {
171 return t.make_block(expanded)
172 }
173 outer_pending := t.pending_stmts.clone()
174 t.pending_stmts.clear()
175 new_if := t.transform_if_branches_with_smartcast(else_id, else_node)
176 mut local_pending := t.pending_stmts.clone()
177 t.pending_stmts = outer_pending
178 if local_pending.len == 0 {
179 return new_if
180 }
181 local_pending << new_if
182 return t.make_block(local_pending)
183}
184
185// expand_map_index_if_guard builds expand map index if guard data for transform.
186fn (mut t Transformer) expand_map_index_if_guard(node flat.Node, lhs_name string, info MapIndexInfo) ?[]flat.NodeId {
187 map_expr := t.stable_expr_for_reuse(info.base_id)
188 key_name := t.new_temp('map_key')
189 ptr_name := t.new_temp('map_ptr')
190 outer_pending := t.pending_stmts.clone()
191 t.pending_stmts.clear()
192 key_expr := t.transform_expr(info.key_id)
193 mut prelude := []flat.NodeId{}
194 t.drain_pending(mut prelude)
195 prelude << t.make_decl_assign_typed(key_name, key_expr, info.key_type)
196 prelude << t.make_decl_assign_typed(ptr_name, t.make_map_get_check_expr(map_expr,
197 info.base_type, key_name), 'voidptr')
198
199 ptr_ident := t.make_ident(ptr_name)
200 found_cond := t.make_infix(.ne, ptr_ident, t.a.add(.nil_literal))
201 ptr_value := t.make_prefix(.mul, t.make_cast('&${info.value_type}', t.make_ident(ptr_name),
202 '&${info.value_type}'))
203 value_decl := t.make_decl_assign_typed(lhs_name, ptr_value, info.value_type)
204
205 then_id := t.a.child(&node, 1)
206 then_node := t.a.nodes[int(then_id)]
207 t.set_var_type(lhs_name, info.value_type)
208 mut then_children := []flat.NodeId{}
209 then_children << value_decl
210 if then_node.kind == .block {
211 then_children << t.transform_stmts(t.a.children_of(&then_node))
212 } else {
213 then_children << t.transform_stmt(then_id)
214 }
215 then_block := t.make_block(then_children)
216
217 mut else_block := flat.empty_node
218 if node.children_count >= 3 {
219 else_id := t.a.child(&node, 2)
220 else_node := t.a.nodes[int(else_id)]
221 else_block = if else_node.kind == .block {
222 t.make_block(t.transform_stmts(t.a.children_of(&else_node)))
223 } else if else_node.kind == .if_expr {
224 t.transform_else_if_expr(else_id, else_node)
225 } else {
226 t.make_block(t.transform_stmt(else_id))
227 }
228 }
229 t.pending_stmts = outer_pending
230 mut expanded := []flat.NodeId{cap: prelude.len + 1}
231 for stmt in prelude {
232 expanded << stmt
233 }
234 expanded << t.make_if(found_cond, then_block, else_block)
235 return expanded
236}
237
238// expand_array_index_if_guard builds expand array index if guard data for transform.
239fn (mut t Transformer) expand_array_index_if_guard(node flat.Node, lhs_name string, info ArrayIndexInfo) ?[]flat.NodeId {
240 array_expr := t.stable_expr_for_reuse(info.base_id)
241 index_name := t.new_temp('arr_idx')
242 outer_pending := t.pending_stmts.clone()
243 t.pending_stmts.clear()
244 index_expr := t.transform_expr(info.index_id)
245 mut prelude := []flat.NodeId{}
246 t.drain_pending(mut prelude)
247 prelude << t.make_decl_assign_typed(index_name, index_expr, 'int')
248
249 idx_ident := t.make_ident(index_name)
250 lower_ok := t.make_infix(.ge, idx_ident, t.make_int_literal(0))
251 upper_ok := t.make_infix(.lt, t.make_ident(index_name), t.make_selector(array_expr, 'len',
252 'int'))
253 found_cond := t.make_infix(.logical_and, lower_ok, upper_ok)
254
255 then_id := t.a.child(&node, 1)
256 then_node := t.a.nodes[int(then_id)]
257 saved_var_types := t.var_types.clone()
258 mut then_children := []flat.NodeId{}
259 mut guard_value_type := info.value_type
260 mut value_expr := t.make_index(array_expr, t.make_ident(index_name), info.value_type)
261 if t.is_optional_type_name(info.value_type) {
262 guard_value_type = t.optional_base_type(t.qualify_optional_type(info.value_type))
263 opt_name := t.new_temp('arr_opt')
264 value_expr = t.make_selector(t.make_ident(opt_name), 'value', guard_value_type)
265 then_children << t.make_decl_assign_typed(opt_name, t.make_index(array_expr,
266 t.make_ident(index_name), info.value_type), info.value_type)
267 }
268 then_children << t.make_decl_assign_typed(lhs_name, value_expr, guard_value_type)
269 t.set_var_type(lhs_name, guard_value_type)
270 if then_node.kind == .block {
271 then_children << t.transform_stmts(t.a.children_of(&then_node))
272 } else {
273 then_children << t.transform_stmt(then_id)
274 }
275 t.var_types = saved_var_types
276 then_block := t.make_block(then_children)
277
278 mut else_block := flat.empty_node
279 if node.children_count >= 3 {
280 else_id := t.a.child(&node, 2)
281 else_node := t.a.nodes[int(else_id)]
282 else_block = if else_node.kind == .block {
283 t.make_block(t.transform_stmts(t.a.children_of(&else_node)))
284 } else if else_node.kind == .if_expr {
285 t.transform_else_if_expr(else_id, else_node)
286 } else {
287 t.make_block(t.transform_stmt(else_id))
288 }
289 }
290 mut selected_block := then_block
291 if t.is_optional_type_name(info.value_type) && then_children.len > 0 {
292 opt_decl := then_children[0]
293 opt_name := t.a.nodes[int(t.a.child(&t.a.nodes[int(opt_decl)], 0))].value
294 ok_cond := t.make_selector(t.make_ident(opt_name), 'ok', 'bool')
295 mut inner_children := []flat.NodeId{}
296 for i in 1 .. then_children.len {
297 inner_children << then_children[i]
298 }
299 inner_if := t.make_if(ok_cond, t.make_block(inner_children), else_block)
300 selected_block = t.make_block([opt_decl, inner_if])
301 }
302 t.pending_stmts = outer_pending
303 mut expanded := []flat.NodeId{cap: prelude.len + 1}
304 for stmt in prelude {
305 expanded << stmt
306 }
307 expanded << t.make_if(found_cond, selected_block, else_block)
308 return expanded
309}
310
311// try_expand_if_expr_value detects an if-expression used as a value and
312// lowers it to a mutable temporary so that the C backend sees a simple
313// variable instead of a gcc statement-expression.
314//
315// x := if cond { a } else { b }
316// becomes:
317// mut __if_tmp_N := zero_value
318// if cond { __if_tmp_N = a } else { __if_tmp_N = b }
319// x := __if_tmp_N
320fn (mut t Transformer) try_expand_if_expr_value(id flat.NodeId, node flat.Node) ?flat.NodeId {
321 if node.kind != .if_expr {
322 return none
323 }
324 // An if-expression used as a value must have both then and else branches.
325 if node.children_count < 3 {
326 return none
327 }
328 mut result_type := t.if_expr_result_type(id, node)
329 if result_type.len == 0 || result_type == 'void' {
330 return none
331 }
332 return t.try_expand_if_expr_value_for_type(id, node, result_type)
333}
334
335// try_expand_if_expr_value_for_type
336// supports helper handling in transform.
337fn (mut t Transformer) try_expand_if_expr_value_for_type(id flat.NodeId, node flat.Node, result_type string) ?flat.NodeId {
338 if node.kind != .if_expr || node.children_count < 3 || result_type.len == 0
339 || result_type == 'void' {
340 return none
341 }
342 mut actual_result_type := result_type
343 branch_type := t.if_expr_branch_result_type(node)
344 if t.if_expr_branch_overrides_sum_target(branch_type, result_type) {
345 actual_result_type = branch_type
346 }
347 tmp_name := t.new_temp('if_val')
348 outer_pending := t.pending_stmts.clone()
349 t.pending_stmts.clear()
350
351 mut prelude := []flat.NodeId{}
352 prelude << t.make_decl_assign_typed(tmp_name, t.zero_value_for_type(actual_result_type),
353 actual_result_type)
354 for stmt in t.build_if_value_chain(id, tmp_name, actual_result_type) {
355 prelude << stmt
356 }
357
358 t.pending_stmts = outer_pending
359 for stmt in prelude {
360 t.pending_stmts << stmt
361 }
362 tmp := t.make_ident(tmp_name)
363 t.a.nodes[int(tmp)].typ = actual_result_type
364 return tmp
365}
366
367// if_expr_branch_overrides_sum_target supports if_expr_branch_overrides_sum_target handling.
368fn (t &Transformer) if_expr_branch_overrides_sum_target(branch_type string, target_type string) bool {
369 if branch_type.len == 0 || target_type.len == 0 {
370 return false
371 }
372 if branch_type.starts_with('[]') {
373 branch_elem := branch_type[2..]
374 target_short := if target_type.contains('.') {
375 target_type.all_after_last('.')
376 } else {
377 target_type
378 }
379 branch_short := if branch_elem.contains('.') {
380 branch_elem.all_after_last('.')
381 } else {
382 branch_elem
383 }
384 if target_short == branch_short {
385 return true
386 }
387 }
388 resolved_target := t.resolve_sum_name(target_type)
389 if resolved_target.len == 0 || resolved_target !in t.sum_types {
390 return false
391 }
392 clean_branch := t.trim_pointer_type(branch_type)
393 branch_sum := t.resolve_sum_name(clean_branch)
394 variant_sum := t.resolve_sum_name(t.find_sum_type_for_variant(clean_branch))
395 return branch_sum != resolved_target && variant_sum != resolved_target
396}
397
398// if_expr_result_type supports if expr result type handling for Transformer.
399fn (t &Transformer) if_expr_result_type(id flat.NodeId, node flat.Node) string {
400 mut node_typ := ''
401 if node.typ.len > 0 {
402 typ := t.normalize_type_alias(node.typ)
403 if typ !in ['array', 'map'] {
404 node_typ = typ
405 }
406 }
407 mut checked_typ := ''
408 if !isnil(t.tc) {
409 if typ := t.tc.expr_type(id) {
410 name := typ.name()
411 if name.len > 0 {
412 checked_typ = t.normalize_type_alias(name)
413 }
414 }
415 }
416 branch_typ := t.if_expr_branch_result_type(node)
417 if branch_typ.starts_with('[]') && t.is_fixed_array_type(checked_typ)
418 && fixed_array_elem_type(checked_typ) == branch_typ[2..] {
419 return branch_typ
420 }
421 if branch_typ.starts_with('[]') && t.is_fixed_array_type(node_typ)
422 && fixed_array_elem_type(node_typ) == branch_typ[2..] {
423 return branch_typ
424 }
425 if branch_typ.starts_with('[]') && !checked_typ.starts_with('[]') && !node_typ.starts_with('[]') {
426 return branch_typ
427 }
428 if t.if_expr_branch_type_overrides(branch_typ, checked_typ) {
429 return branch_typ
430 }
431 if t.if_expr_branch_type_overrides(branch_typ, node_typ) {
432 return branch_typ
433 }
434 if checked_typ.len > 0 && checked_typ !in ['array', 'map'] {
435 return checked_typ
436 }
437 if node_typ.len > 0 {
438 return node_typ
439 }
440 if branch_typ.len > 0 {
441 return branch_typ
442 }
443 return ''
444}
445
446// if_expr_branch_type_overrides supports if expr branch type overrides handling for Transformer.
447fn (t &Transformer) if_expr_branch_type_overrides(branch_typ string, stale_typ string) bool {
448 if branch_typ.len == 0 || stale_typ.len == 0 || branch_typ == stale_typ {
449 return false
450 }
451 if stale_typ in ['array', 'map', 'unknown'] {
452 return true
453 }
454 if stale_typ in t.enum_types && branch_typ == 'int' {
455 return false
456 }
457 if stale_typ.starts_with('&') && branch_typ.starts_with('&') {
458 stale_inner := stale_typ[1..]
459 branch_inner := branch_typ[1..]
460 if stale_inner in ['int', 'void', 'unknown'] && branch_inner !in ['int', 'void', 'unknown'] {
461 return true
462 }
463 }
464 if stale_typ.starts_with('[]') || t.is_fixed_array_type(stale_typ) {
465 return !branch_typ.starts_with('[]') && !t.is_fixed_array_type(branch_typ)
466 }
467 if stale_typ.starts_with('map[') {
468 return !branch_typ.starts_with('map[')
469 }
470 return false
471}
472
473// if_expr_branch_result_type supports if expr branch result type handling for Transformer.
474fn (t &Transformer) if_expr_branch_result_type(node flat.Node) string {
475 mut result := ''
476 if node.children_count >= 2 {
477 then_smartcasts :=
478 t.smartcast_contexts_from_is_exprs(t.extract_all_is_exprs(t.a.child(&node, 0)))
479 then_type := t.stmt_value_type_with_smartcasts(t.a.child(&node, 1), then_smartcasts)
480 if then_type.len > 0 {
481 result = t.merge_if_expr_types(result, t.normalize_type_alias(then_type))
482 }
483 }
484 if node.children_count >= 3 {
485 else_id := t.a.child(&node, 2)
486 else_node := t.a.nodes[int(else_id)]
487 else_type := if else_node.kind == .if_expr {
488 t.if_expr_result_type(else_id, else_node)
489 } else {
490 t.stmt_value_type(else_id)
491 }
492 if else_type.len > 0 {
493 result = t.merge_if_expr_types(result, t.normalize_type_alias(else_type))
494 }
495 }
496 return result
497}
498
499// smartcast_contexts_from_is_exprs converts smartcast contexts from is exprs data for transform.
500fn (t &Transformer) smartcast_contexts_from_is_exprs(infos []IsExprInfo) []SmartcastContext {
501 mut result := []SmartcastContext{cap: infos.len}
502 for info in infos {
503 result << SmartcastContext{
504 expr_name: info.expr_name
505 variant_name: info.variant_name
506 sum_type_name: info.sum_type_name
507 }
508 }
509 return result
510}
511
512// stmt_value_type_with_smartcasts
513// supports helper handling in transform.
514fn (t &Transformer) stmt_value_type_with_smartcasts(id flat.NodeId, contexts []SmartcastContext) string {
515 if int(id) < 0 {
516 return ''
517 }
518 node := t.a.nodes[int(id)]
519 match node.kind {
520 .expr_stmt {
521 if node.children_count > 0 {
522 return t.node_type_with_smartcasts(t.a.child(&node, node.children_count - 1),
523 contexts)
524 }
525 return ''
526 }
527 .block {
528 for i := node.children_count - 1; i >= 0; i-- {
529 typ := t.stmt_value_type_with_smartcasts(t.a.child(&node, i), contexts)
530 if typ.len > 0 {
531 return typ
532 }
533 }
534 return ''
535 }
536 else {
537 return t.node_type_with_smartcasts(id, contexts)
538 }
539 }
540}
541
542// node_type_with_smartcasts supports node type with smartcasts handling for Transformer.
543fn (t &Transformer) node_type_with_smartcasts(id flat.NodeId, contexts []SmartcastContext) string {
544 if int(id) < 0 {
545 return ''
546 }
547 node := t.a.nodes[int(id)]
548 match node.kind {
549 .ident {
550 if sc := t.find_smartcast_in_context(node.value, contexts) {
551 return t.smartcast_target_type(sc)
552 }
553 return t.node_type(id)
554 }
555 .selector {
556 if node.children_count == 0 {
557 return t.node_type(id)
558 }
559 full_key := t.expr_key(id)
560 if full_key.len > 0 {
561 if sc := t.find_smartcast_in_context(full_key, contexts) {
562 return t.smartcast_target_type(sc)
563 }
564 }
565 base_id := t.a.child(&node, 0)
566 base_key := t.expr_key(base_id)
567 if base_key.len > 0 {
568 if sc := t.find_smartcast_in_context(base_key, contexts) {
569 variant_type := t.qualify_variant(sc.variant_name, sc.sum_type_name)
570 if ftyp := t.lookup_struct_field_type(variant_type, node.value) {
571 return ftyp
572 }
573 if ftyp := t.lookup_struct_field_type(sc.variant_name, node.value) {
574 return ftyp
575 }
576 }
577 }
578 base_type := t.node_type_with_smartcasts(base_id, contexts)
579 clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
580 if ftyp := t.lookup_struct_field_type(clean_base, node.value) {
581 return ftyp
582 }
583 return t.node_type(id)
584 }
585 .prefix {
586 if node.children_count == 0 {
587 return t.node_type(id)
588 }
589 child_type := t.node_type_with_smartcasts(t.a.child(&node, 0), contexts)
590 if node.op == .amp && child_type.len > 0 {
591 return '&${child_type}'
592 }
593 if node.op == .mul && child_type.starts_with('&') {
594 return child_type[1..]
595 }
596 if node.op == .not {
597 return 'bool'
598 }
599 return t.node_type(id)
600 }
601 .paren {
602 if node.children_count > 0 {
603 return t.node_type_with_smartcasts(t.a.child(&node, 0), contexts)
604 }
605 return t.node_type(id)
606 }
607 .array_literal {
608 if node.typ.len > 0 {
609 typ := t.normalize_type_alias(node.typ)
610 if typ != 'array' {
611 return typ
612 }
613 }
614 if node.children_count > 0 {
615 elem_type := t.node_type_with_smartcasts(t.a.child(&node, 0), contexts)
616 if elem_type.len > 0 {
617 return '[]${elem_type}'
618 }
619 }
620 return t.node_type(id)
621 }
622 .index {
623 base_type := if node.children_count > 0 {
624 t.node_type_with_smartcasts(t.a.child(&node, 0), contexts)
625 } else {
626 ''
627 }
628 if node.value == 'range' && base_type.starts_with('[]') {
629 return base_type
630 }
631 if base_type.starts_with('[]') {
632 return base_type[2..]
633 }
634 return t.node_type(id)
635 }
636 .if_expr {
637 return t.if_expr_result_type(id, node)
638 }
639 .match_stmt {
640 return t.match_expr_type(node)
641 }
642 else {
643 return t.node_type(id)
644 }
645 }
646}
647
648// find_smartcast_in_context resolves find smartcast in context information for transform.
649fn (t &Transformer) find_smartcast_in_context(expr_name string, contexts []SmartcastContext) ?SmartcastContext {
650 mut i := contexts.len - 1
651 for i >= 0 {
652 if contexts[i].expr_name == expr_name {
653 return contexts[i]
654 }
655 i--
656 }
657 return t.find_smartcast(expr_name)
658}
659
660// merge_if_expr_types supports merge if expr types handling for Transformer.
661fn (t &Transformer) merge_if_expr_types(current string, next string) string {
662 if current.len == 0 {
663 return next
664 }
665 if next.len == 0 || current == next {
666 return current
667 }
668 if current == 'array' && next.starts_with('[]') {
669 return next
670 }
671 if next == 'array' && current.starts_with('[]') {
672 return current
673 }
674 if current.starts_with('[]') && t.is_fixed_array_type(next)
675 && current[2..] == fixed_array_elem_type(next) {
676 return current
677 }
678 if next.starts_with('[]') && t.is_fixed_array_type(current)
679 && next[2..] == fixed_array_elem_type(current) {
680 return next
681 }
682 if current.starts_with('[]') && !next.starts_with('[]') && current[2..] == next {
683 return current
684 }
685 if next.starts_with('[]') && !current.starts_with('[]') && next[2..] == current {
686 return next
687 }
688 return current
689}
690
691// build_if_value_chain builds if value chain data for transform.
692fn (mut t Transformer) build_if_value_chain(if_id flat.NodeId, target_name string, target_type string) []flat.NodeId {
693 if_node := t.a.nodes[int(if_id)]
694 if if_node.kind != .if_expr || if_node.children_count < 2 {
695 return []flat.NodeId{}
696 }
697 if guard_chain := t.build_if_value_guard_chain(if_node, target_name, target_type) {
698 return guard_chain
699 }
700 cond_id := t.a.child(&if_node, 0)
701 then_id := t.a.child(&if_node, 1)
702 has_else := if_node.children_count >= 3
703
704 all_is := t.extract_all_is_exprs(cond_id)
705 new_cond := t.transform_and_chain_smartcasts(cond_id)
706 mut result := []flat.NodeId{}
707 t.drain_pending(mut result)
708
709 for info in all_is {
710 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
711 }
712 then_block := t.if_value_branch_block(then_id, target_name, target_type)
713 for _ in all_is {
714 t.pop_smartcast()
715 }
716
717 mut else_block := flat.empty_node
718 if has_else {
719 else_id := t.a.child(&if_node, 2)
720 else_node := t.a.nodes[int(else_id)]
721 if else_node.kind == .if_expr {
722 else_block = t.make_block(t.build_if_value_chain(else_id, target_name, target_type))
723 } else {
724 else_block = t.if_value_branch_block(else_id, target_name, target_type)
725 }
726 }
727 result << t.make_if(new_cond, then_block, else_block)
728 return result
729}
730
731// build_if_value_guard_chain builds if value guard chain data for transform.
732fn (mut t Transformer) build_if_value_guard_chain(if_node flat.Node, target_name string, target_type string) ?[]flat.NodeId {
733 if if_node.children_count < 3 {
734 return none
735 }
736 cond_id := t.a.child(&if_node, 0)
737 cond := t.a.nodes[int(cond_id)]
738 if cond.kind != .decl_assign || cond.children_count < 2 {
739 return none
740 }
741 lhs_id := t.a.child(&cond, 0)
742 rhs_id := t.a.child(&cond, 1)
743 lhs := t.a.nodes[int(lhs_id)]
744 if lhs.kind != .ident || lhs.value.len == 0 {
745 return none
746 }
747 if info := t.map_index_info(rhs_id) {
748 return t.build_map_index_if_value_guard_chain(if_node, lhs.value, info, target_name,
749 target_type)
750 }
751 if info := t.array_index_info(rhs_id) {
752 return t.build_array_index_if_value_guard_chain(if_node, lhs.value, info, target_name,
753 target_type)
754 }
755 mut rhs_type := t.optional_result_expr_type_name(rhs_id)
756 if !t.is_optional_type_name(rhs_type) {
757 return none
758 }
759 rhs_type = t.qualify_optional_type(rhs_type)
760 value_type := t.optional_base_type(rhs_type)
761 tmp_name := t.new_temp('if_guard')
762 rhs_expr := t.transform_expr(rhs_id)
763 mut result := []flat.NodeId{}
764 t.drain_pending(mut result)
765 result << t.make_decl_assign_typed(tmp_name, rhs_expr, rhs_type)
766 ok_cond := t.make_selector(t.make_ident(tmp_name), 'ok', 'bool')
767 value_decl := t.make_decl_assign_typed(lhs.value, t.make_selector(t.make_ident(tmp_name),
768 'value', value_type), value_type)
769
770 saved_var_types := t.var_types.clone()
771 t.set_var_type(lhs.value, value_type)
772 then_id := t.a.child(&if_node, 1)
773 then_block0 := t.if_value_branch_block(then_id, target_name, target_type)
774 mut then_children := []flat.NodeId{cap: int(t.a.nodes[int(then_block0)].children_count) + 1}
775 then_children << value_decl
776 then_children << t.a.children_of(&t.a.nodes[int(then_block0)])
777 then_block := t.make_block(then_children)
778 t.var_types = saved_var_types
779
780 else_id := t.a.child(&if_node, 2)
781 else_node := t.a.nodes[int(else_id)]
782 else_block := if else_node.kind == .if_expr {
783 t.make_block(t.build_if_value_chain(else_id, target_name, target_type))
784 } else {
785 t.if_value_branch_block(else_id, target_name, target_type)
786 }
787 result << t.make_if(ok_cond, then_block, else_block)
788 return result
789}
790
791// build_map_index_if_value_guard_chain supports build_map_index_if_value_guard_chain handling.
792fn (mut t Transformer) build_map_index_if_value_guard_chain(if_node flat.Node, lhs_name string, info MapIndexInfo, target_name string, target_type string) []flat.NodeId {
793 map_expr := t.stable_expr_for_reuse(info.base_id)
794 key_name := t.new_temp('map_key')
795 ptr_name := t.new_temp('map_ptr')
796 outer_pending := t.pending_stmts.clone()
797 t.pending_stmts.clear()
798 key_expr := t.transform_expr(info.key_id)
799 mut result := []flat.NodeId{}
800 t.drain_pending(mut result)
801 result << t.make_decl_assign_typed(key_name, key_expr, info.key_type)
802 result << t.make_decl_assign_typed(ptr_name, t.make_map_get_check_expr(map_expr,
803 info.base_type, key_name), 'voidptr')
804 ptr_ident := t.make_ident(ptr_name)
805 found_cond := t.make_infix(.ne, ptr_ident, t.a.add(.nil_literal))
806
807 saved_var_types := t.var_types.clone()
808 mut then_children := []flat.NodeId{}
809 if lhs_name != '_' {
810 ptr_value := t.make_prefix(.mul, t.make_cast('&${info.value_type}', t.make_ident(ptr_name),
811 '&${info.value_type}'))
812 then_children << t.make_decl_assign_typed(lhs_name, ptr_value, info.value_type)
813 t.set_var_type(lhs_name, info.value_type)
814 }
815 then_id := t.a.child(&if_node, 1)
816 then_block0 := t.if_value_branch_block(then_id, target_name, target_type)
817 then_children << t.a.children_of(&t.a.nodes[int(then_block0)])
818 then_block := t.make_block(then_children)
819 t.var_types = saved_var_types
820
821 else_id := t.a.child(&if_node, 2)
822 else_node := t.a.nodes[int(else_id)]
823 else_block := if else_node.kind == .if_expr {
824 t.make_block(t.build_if_value_chain(else_id, target_name, target_type))
825 } else {
826 t.if_value_branch_block(else_id, target_name, target_type)
827 }
828 t.pending_stmts = outer_pending
829 result << t.make_if(found_cond, then_block, else_block)
830 return result
831}
832
833// build_array_index_if_value_guard_chain supports build_array_index_if_value_guard_chain handling.
834fn (mut t Transformer) build_array_index_if_value_guard_chain(if_node flat.Node, lhs_name string, info ArrayIndexInfo, target_name string, target_type string) []flat.NodeId {
835 array_expr := t.stable_expr_for_reuse(info.base_id)
836 index_name := t.new_temp('arr_idx')
837 outer_pending := t.pending_stmts.clone()
838 t.pending_stmts.clear()
839 index_expr := t.transform_expr(info.index_id)
840 mut result := []flat.NodeId{}
841 t.drain_pending(mut result)
842 result << t.make_decl_assign_typed(index_name, index_expr, 'int')
843 idx_ident := t.make_ident(index_name)
844 lower_ok := t.make_infix(.ge, idx_ident, t.make_int_literal(0))
845 upper_ok := t.make_infix(.lt, t.make_ident(index_name), t.make_selector(array_expr, 'len',
846 'int'))
847 found_cond := t.make_infix(.logical_and, lower_ok, upper_ok)
848
849 saved_var_types := t.var_types.clone()
850 mut then_children := []flat.NodeId{}
851 mut opt_decl := flat.empty_node
852 mut opt_name := ''
853 if lhs_name != '_' {
854 mut value_type := info.value_type
855 mut value := t.make_index(array_expr, t.make_ident(index_name), info.value_type)
856 if t.is_optional_type_name(info.value_type) {
857 value_type = t.optional_base_type(t.qualify_optional_type(info.value_type))
858 opt_name = t.new_temp('arr_opt')
859 opt_decl = t.make_decl_assign_typed(opt_name, t.make_index(array_expr,
860 t.make_ident(index_name), info.value_type), info.value_type)
861 value = t.make_selector(t.make_ident(opt_name), 'value', value_type)
862 }
863 then_children << t.make_decl_assign_typed(lhs_name, value, value_type)
864 t.set_var_type(lhs_name, value_type)
865 } else if t.is_optional_type_name(info.value_type) {
866 opt_name = t.new_temp('arr_opt')
867 opt_decl = t.make_decl_assign_typed(opt_name, t.make_index(array_expr,
868 t.make_ident(index_name), info.value_type), info.value_type)
869 }
870 then_id := t.a.child(&if_node, 1)
871 then_block0 := t.if_value_branch_block(then_id, target_name, target_type)
872 then_children << t.a.children_of(&t.a.nodes[int(then_block0)])
873 then_block := t.make_block(then_children)
874 t.var_types = saved_var_types
875
876 else_id := t.a.child(&if_node, 2)
877 else_node := t.a.nodes[int(else_id)]
878 else_block := if else_node.kind == .if_expr {
879 t.make_block(t.build_if_value_chain(else_id, target_name, target_type))
880 } else {
881 t.if_value_branch_block(else_id, target_name, target_type)
882 }
883 t.pending_stmts = outer_pending
884 mut selected_block := then_block
885 if t.is_optional_type_name(info.value_type) && opt_name.len > 0 {
886 ok_cond := t.make_selector(t.make_ident(opt_name), 'ok', 'bool')
887 inner_if := t.make_if(ok_cond, then_block, else_block)
888 selected_block = t.make_block([opt_decl, inner_if])
889 }
890 result << t.make_if(found_cond, selected_block, else_block)
891 return result
892}
893
894// if_value_branch_block supports if value branch block handling for Transformer.
895fn (mut t Transformer) if_value_branch_block(branch_id flat.NodeId, target_name string, target_type string) flat.NodeId {
896 if int(branch_id) < 0 {
897 return t.make_block([]flat.NodeId{})
898 }
899 branch := t.a.nodes[int(branch_id)]
900 if branch.kind == .if_expr {
901 return t.make_block(t.build_if_value_chain(branch_id, target_name, target_type))
902 }
903 if branch.kind != .block {
904 mut result := []flat.NodeId{}
905 value := t.transform_if_branch_value(branch_id, target_type)
906 t.drain_pending(mut result)
907 result << t.make_assign(t.make_ident(target_name), value)
908 return t.make_block(result)
909 }
910 if branch.children_count == 0 {
911 return t.make_block([]flat.NodeId{})
912 }
913
914 mut stmt_ids := []flat.NodeId{cap: int(branch.children_count)}
915 for i in 0 .. branch.children_count {
916 stmt_ids << t.a.child(&branch, i)
917 }
918 mut result := []flat.NodeId{}
919 if stmt_ids.len > 1 {
920 for stmt in t.transform_stmts(stmt_ids[..stmt_ids.len - 1]) {
921 result << stmt
922 }
923 }
924
925 tail_id := stmt_ids[stmt_ids.len - 1]
926 tail := t.a.nodes[int(tail_id)]
927 if tail.kind == .return_stmt {
928 tail_stmts := t.transform_stmt(tail_id)
929 t.drain_pending(mut result)
930 for stmt in tail_stmts {
931 result << stmt
932 }
933 return t.make_block(result)
934 }
935 if tail.kind == .expr_stmt && tail.children_count > 0 {
936 inner_id := t.a.child(&tail, 0)
937 inner := t.a.nodes[int(inner_id)]
938 if inner.kind == .call && t.is_noreturn_call(inner) {
939 tail_stmts := t.transform_stmt(tail_id)
940 t.drain_pending(mut result)
941 for stmt in tail_stmts {
942 result << stmt
943 }
944 return t.make_block(result)
945 }
946 if t.node_type(inner_id) == 'void' {
947 tail_stmts := t.transform_stmt(tail_id)
948 t.drain_pending(mut result)
949 for stmt in tail_stmts {
950 result << stmt
951 }
952 return t.make_block(result)
953 }
954 value := t.transform_if_branch_value(inner_id, target_type)
955 t.drain_pending(mut result)
956 result << t.make_assign(t.make_ident(target_name), value)
957 return t.make_block(result)
958 }
959 if tail.kind == .block && t.stmt_value_type(tail_id).len > 0 {
960 value := t.transform_if_branch_value(tail_id, target_type)
961 t.drain_pending(mut result)
962 result << t.make_assign(t.make_ident(target_name), value)
963 return t.make_block(result)
964 }
965 if tail.kind == .match_stmt {
966 value := t.transform_if_branch_value(tail_id, target_type)
967 t.drain_pending(mut result)
968 result << t.make_assign(t.make_ident(target_name), value)
969 return t.make_block(result)
970 }
971 if tail.kind == .if_expr {
972 value := t.transform_if_branch_value(tail_id, target_type)
973 t.drain_pending(mut result)
974 result << t.make_assign(t.make_ident(target_name), value)
975 return t.make_block(result)
976 }
977 if t.is_stmt_kind(tail.kind) {
978 tail_stmts := t.transform_stmt(tail_id)
979 t.drain_pending(mut result)
980 for stmt in tail_stmts {
981 result << stmt
982 }
983 return t.make_block(result)
984 }
985 value := t.transform_if_branch_value(tail_id, target_type)
986 t.drain_pending(mut result)
987 result << t.make_assign(t.make_ident(target_name), value)
988 return t.make_block(result)
989}
990
991// transform_if_branch_value transforms transform if branch value data for transform.
992fn (mut t Transformer) transform_if_branch_value(id flat.NodeId, target_type string) flat.NodeId {
993 if t.is_sum_type_name(target_type) {
994 return t.wrap_sum_value(id, target_type)
995 }
996 if converted := t.fixed_array_value_to_dynamic(id, target_type) {
997 return converted
998 }
999 return t.transform_expr_for_type(id, target_type)
1000}
1001
1002// transform_is_condition transforms an `x is Type` condition node into the
1003// common sum-type tag comparison used by all C emission paths.
1004fn (mut t Transformer) transform_is_condition(cond_id flat.NodeId) flat.NodeId {
1005 if int(cond_id) < 0 {
1006 return cond_id
1007 }
1008 cond := t.a.nodes[int(cond_id)]
1009 if cond.kind != .is_expr {
1010 return cond_id
1011 }
1012 // is_expr: child[0] is the expression being checked, value is the type name.
1013 if cond.children_count < 1 {
1014 return cond_id
1015 }
1016 variant_name := cond.value
1017 if variant_name.len == 0 {
1018 return cond_id
1019 }
1020 return t.transform_is_expr(cond_id, cond)
1021}
1022
1023// transform_and_chain_smartcasts handles conditions like
1024// `x is T && x.field > 0` where the second operand should see x
1025// narrowed to T through a smartcast.
1026//
1027// It walks a left-associative chain of .logical_and nodes. When a
1028// term is an is_expr it pushes a smartcast so subsequent terms
1029// (and the then-branch) see the narrowed type.
1030//
1031// Currently passes through unchanged but is structured to detect
1032// the pattern for future expansion.
1033fn (mut t Transformer) transform_and_chain_smartcasts(cond_id flat.NodeId) flat.NodeId {
1034 if int(cond_id) < 0 {
1035 return cond_id
1036 }
1037 cond := t.a.nodes[int(cond_id)]
1038 if cond.kind == .decl_assign {
1039 return t.transform_if_guard_condition(cond)
1040 }
1041 if cond.kind != .infix || cond.op !in [.logical_and, .logical_or] {
1042 // Not an && chain -- preserve bare smartcast checks and fully transform
1043 // ordinary conditions.
1044 if cond.kind == .is_expr {
1045 return t.transform_is_condition(cond_id)
1046 }
1047 return t.transform_expr(cond_id)
1048 }
1049 if cond.children_count < 2 {
1050 return cond_id
1051 }
1052 lhs_id := t.a.child(&cond, 0)
1053 rhs_id := t.a.child(&cond, 1)
1054 lhs := t.a.nodes[int(lhs_id)]
1055 new_lhs := t.transform_and_chain_smartcasts(lhs_id)
1056 lhs_pending := t.pending_stmts.clone()
1057 t.pending_stmts.clear()
1058 lhs_smartcasts := if cond.op == .logical_and {
1059 t.extract_all_is_exprs(lhs_id)
1060 } else {
1061 []IsExprInfo{}
1062 }
1063 for info in lhs_smartcasts {
1064 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
1065 }
1066 mut new_rhs := t.transform_and_chain_smartcasts(rhs_id)
1067 rhs_pending := t.pending_stmts.clone()
1068 t.pending_stmts.clear()
1069 for _ in lhs_smartcasts {
1070 t.pop_smartcast()
1071 }
1072 // The left operand always evaluates, so its pending statements may run before
1073 // the `&&`/`||`. The right operand is conditional, so any statements its
1074 // lowering produced (e.g. an `in [...]` / `or {}` temporary) must run only when
1075 // the right operand is actually reached — wrap them in a `({ ...; value; })`
1076 // statement-expression to preserve short-circuit evaluation.
1077 for stmt in lhs_pending {
1078 t.pending_stmts << stmt
1079 }
1080 if rhs_pending.len > 0 {
1081 mut block_stmts := rhs_pending.clone()
1082 block_stmts << t.make_expr_stmt(new_rhs)
1083 new_rhs = t.make_block(block_stmts)
1084 t.a.nodes[int(new_rhs)].typ = 'bool'
1085 }
1086 if lhs.kind == .is_expr && new_lhs == lhs_id && new_rhs == rhs_id {
1087 return cond_id
1088 }
1089 return t.make_infix(cond.op, new_lhs, new_rhs)
1090}
1091
1092// transform_if_guard_condition transforms transform if guard condition data for transform.
1093fn (mut t Transformer) transform_if_guard_condition(node flat.Node) flat.NodeId {
1094 if node.kind != .decl_assign || node.children_count < 2 {
1095 return flat.empty_node
1096 }
1097 mut new_children := []flat.NodeId{cap: int(node.children_count)}
1098 for i in 0 .. node.children_count {
1099 child_id := t.a.child(&node, i)
1100 if i == 1 {
1101 new_children << t.transform_expr(child_id)
1102 } else {
1103 new_children << t.transform_lvalue(child_id)
1104 }
1105 }
1106 start := t.a.children.len
1107 for child in new_children {
1108 t.a.children << child
1109 }
1110 return t.a.add_node(flat.Node{
1111 kind: .decl_assign
1112 op: node.op
1113 children_start: start
1114 children_count: flat.child_count(new_children.len)
1115 pos: node.pos
1116 value: node.value
1117 typ: node.typ
1118 })
1119}
1120
1121// transform_if_branches_with_smartcast is the main if-expr handler that
1122// integrates smartcasting. When the condition contains an is_expr, it
1123// pushes a smartcast for the then-branch, transforms the body under
1124// that context, pops the smartcast, then transforms the else-block.
1125fn (mut t Transformer) transform_if_branches_with_smartcast(id flat.NodeId, node flat.Node) flat.NodeId {
1126 if node.kind != .if_expr || node.children_count < 2 {
1127 return id
1128 }
1129 cond_id := t.a.child(&node, 0)
1130 then_id := t.a.child(&node, 1)
1131 has_else := node.children_count >= 3
1132 else_id := if has_else { t.a.child(&node, 2) } else { flat.empty_node }
1133
1134 all_is := t.extract_all_is_exprs(cond_id)
1135 new_cond_id := t.transform_and_chain_smartcasts(cond_id)
1136 cond_pending := t.pending_stmts.clone()
1137 t.pending_stmts.clear()
1138
1139 // Transform then-block children under the smartcast context.
1140 saved_var_types := t.var_types.clone()
1141 for info in all_is {
1142 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
1143 }
1144 then_node := t.a.nodes[int(then_id)]
1145 mut new_then_id := then_id
1146 if then_node.kind == .block {
1147 child_ids := t.a.children_of(&then_node)
1148 new_children := t.transform_stmts(child_ids)
1149 block_start := t.a.children.len
1150 for c in new_children {
1151 t.a.children << c
1152 }
1153 new_then_id = t.a.add_node(flat.Node{
1154 kind: .block
1155 children_start: block_start
1156 children_count: flat.child_count(new_children.len)
1157 })
1158 }
1159
1160 for _ in all_is {
1161 t.pop_smartcast()
1162 }
1163 t.var_types = saved_var_types
1164
1165 // Transform else-block (no smartcast -- the is_expr was false here).
1166 mut new_else_id := flat.empty_node
1167 if has_else {
1168 else_node := t.a.nodes[int(else_id)]
1169 if else_node.kind == .if_expr {
1170 // else-if chain: recurse.
1171 new_else_id = t.transform_else_if_expr(else_id, else_node)
1172 } else if else_node.kind == .block {
1173 child_ids := t.a.children_of(&else_node)
1174 new_children := t.transform_stmts(child_ids)
1175 block_start := t.a.children.len
1176 for c in new_children {
1177 t.a.children << c
1178 }
1179 new_else_id = t.a.add_node(flat.Node{
1180 kind: .block
1181 children_start: block_start
1182 children_count: flat.child_count(new_children.len)
1183 })
1184 } else {
1185 new_else_id = else_id
1186 }
1187 }
1188
1189 // Rebuild the if_expr with (possibly) new children.
1190 if_start := t.a.children.len
1191 t.a.children << new_cond_id
1192 t.a.children << new_then_id
1193 mut child_count := 2
1194 if has_else {
1195 t.a.children << new_else_id
1196 child_count = 3
1197 }
1198 new_if := t.a.add_node(flat.Node{
1199 kind: .if_expr
1200 children_start: if_start
1201 children_count: flat.child_count(child_count)
1202 typ: node.typ
1203 pos: node.pos
1204 })
1205 for pending in cond_pending {
1206 t.pending_stmts << pending
1207 }
1208 return new_if
1209}
1210
1211// --- helpers ---
1212
1213// IsExprInfo stores is expr info metadata used by transform.
1214struct IsExprInfo {
1215 expr_name string
1216 variant_name string
1217 sum_type_name string
1218}
1219
1220// extract_all_is_exprs supports extract all is exprs handling for Transformer.
1221fn (t &Transformer) extract_all_is_exprs(cond_id flat.NodeId) []IsExprInfo {
1222 mut result := []IsExprInfo{}
1223 t.collect_is_exprs(cond_id, mut result)
1224 return result
1225}
1226
1227// collect_is_exprs updates collect is exprs state for transform.
1228fn (t &Transformer) collect_is_exprs(cond_id flat.NodeId, mut result []IsExprInfo) {
1229 if int(cond_id) < 0 {
1230 return
1231 }
1232 cond := t.a.nodes[int(cond_id)]
1233 if cond.kind == .is_expr && cond.children_count >= 1 {
1234 expr_id := t.a.child(&cond, 0)
1235 ek := t.expr_key(expr_id)
1236 if ek.len > 0 && cond.value.len > 0 {
1237 expr_type := t.original_expr_type(expr_id)
1238 stn := t.sum_type_for_is_expr(expr_type, cond.value)
1239 if stn.len > 0 {
1240 result << IsExprInfo{
1241 expr_name: ek
1242 variant_name: cond.value
1243 sum_type_name: stn
1244 }
1245 } else {
1246 if t.is_interface_type_name(expr_type) {
1247 result << IsExprInfo{
1248 expr_name: ek
1249 variant_name: cond.value
1250 sum_type_name: expr_type
1251 }
1252 }
1253 }
1254 }
1255 }
1256 if cond.kind == .infix && cond.op == .logical_and && cond.children_count >= 2 {
1257 t.collect_is_exprs(t.a.child(&cond, 0), mut result)
1258 t.collect_is_exprs(t.a.child(&cond, 1), mut result)
1259 }
1260}
1261
1262// extract_is_expr supports extract is expr handling for Transformer.
1263fn (t &Transformer) extract_is_expr(cond_id flat.NodeId) IsExprInfo {
1264 if int(cond_id) < 0 {
1265 return IsExprInfo{}
1266 }
1267 cond := t.a.nodes[int(cond_id)]
1268 if cond.kind == .is_expr && cond.children_count >= 1 {
1269 expr_id := t.a.child(&cond, 0)
1270 ek := t.expr_key(expr_id)
1271 if ek.len > 0 && cond.value.len > 0 {
1272 expr_type := t.original_expr_type(expr_id)
1273 return IsExprInfo{
1274 expr_name: ek
1275 variant_name: cond.value
1276 sum_type_name: t.sum_type_for_is_expr(expr_type, cond.value)
1277 }
1278 }
1279 }
1280 if cond.kind == .infix && cond.op == .logical_and && cond.children_count >= 2 {
1281 // Check left side first (is_expr is typically on the left of &&).
1282 lhs_id := t.a.child(&cond, 0)
1283 info := t.extract_is_expr(lhs_id)
1284 if info.expr_name.len > 0 {
1285 return info
1286 }
1287 // Check right side as fallback.
1288 rhs_id := t.a.child(&cond, 1)
1289 return t.extract_is_expr(rhs_id)
1290 }
1291 return IsExprInfo{}
1292}
1293
1294// sum_type_for_is_expr supports sum type for is expr handling for Transformer.
1295fn (t &Transformer) sum_type_for_is_expr(expr_type string, variant string) string {
1296 clean_expr_type := t.trim_pointer_type(expr_type)
1297 resolved_expr_sum := t.resolve_sum_name(clean_expr_type)
1298 if resolved_expr_sum in t.sum_types {
1299 if _ := t.sum_variant_name(resolved_expr_sum, variant) {
1300 return clean_expr_type
1301 }
1302 }
1303 return t.find_sum_type_for_variant(variant)
1304}
1305
1306// find_sum_type_for_variant returns the sum type name that contains
1307// the given variant, or '' if none is found.
1308fn (t &Transformer) find_sum_type_for_variant(variant string) string {
1309 mut best := ''
1310 for sum_name, variants in t.sum_types {
1311 for v in variants {
1312 if t.variant_names_match(v, variant) {
1313 if sum_name.contains('.') {
1314 return sum_name
1315 }
1316 if best.len == 0 {
1317 best = sum_name
1318 }
1319 }
1320 }
1321 }
1322 return best
1323}
1324