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