module transform import v3.flat // trim_pointer_type transforms trim pointer type data for transform. fn (t &Transformer) trim_pointer_type(typ string) string { if typ.starts_with('&') { return typ[1..] } return typ } // resolve_variant resolves resolve variant information for transform. fn (t &Transformer) resolve_variant(sum_name string, variant string) string { if variant.contains('.') { return variant } resolved_sum := t.resolve_sum_name(sum_name) if resolved_variant := t.sum_variant_name(resolved_sum, variant) { return resolved_variant } if resolved_sum.contains('.') { return '${resolved_sum.all_before_last('.')}.${variant}' } if sum_name.contains('.') { return '${sum_name.all_before_last('.')}.${variant}' } return variant } // resolve_sum_name resolves resolve sum name information for transform. fn (t &Transformer) resolve_sum_name(sum_name string) string { if sum_name.len == 0 { return sum_name } if isnil(t.sum_cache) { return t.resolve_sum_name_uncached(sum_name) } mut c := t.sum_cache if c.module != t.cur_module { c.module = t.cur_module c.entries.clear() } if cached := c.entries[sum_name] { return cached } result := t.resolve_sum_name_uncached(sum_name) c.entries[sum_name] = result return result } // resolve_sum_name_uncached resolves resolve sum name uncached information for transform. fn (t &Transformer) resolve_sum_name_uncached(sum_name string) string { if sum_name in t.sum_types { return sum_name } if sum_name.contains('.') { short_sum := sum_name.all_after_last('.') if short_sum in t.sum_types { return short_sum } } if !sum_name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' { qsum := '${t.cur_module}.${sum_name}' if qsum in t.sum_types { return qsum } } if !sum_name.contains('.') { mut found := '' for key, _ in t.sum_types { if key.contains('.') && key.all_after_last('.') == sum_name { if found.len > 0 && found != key { found = '' break } found = key } } if found.len > 0 { return found } } if !isnil(t.tc) { if sum_name in t.tc.sum_types { return sum_name } if sum_name.contains('.') { short_sum := sum_name.all_after_last('.') if short_sum in t.tc.sum_types { return short_sum } } if !sum_name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' { qsum := '${t.cur_module}.${sum_name}' if qsum in t.tc.sum_types { return qsum } } if !sum_name.contains('.') { mut found := '' for key, _ in t.tc.sum_types { if key.contains('.') && key.all_after_last('.') == sum_name { if found.len > 0 && found != key { found = '' break } found = key } } if found.len > 0 { return found } } } return sum_name } // is_sum_type_name reports whether is sum type name applies in transform. fn (t &Transformer) is_sum_type_name(name string) bool { if name.len == 0 { return false } resolved := t.resolve_sum_name(name) return resolved in t.sum_types } // is_interface_type_name reports whether is interface type name applies in transform. fn (t &Transformer) is_interface_type_name(name string) bool { if name.len == 0 || isnil(t.tc) { return false } clean := t.trim_pointer_type(t.normalize_type_alias(name)) if clean == 'IError' || clean in t.tc.interface_names { return true } if !clean.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' { return '${t.cur_module}.${clean}' in t.tc.interface_names } return false } // interface_variant_type supports interface variant type handling for Transformer. fn (t &Transformer) interface_variant_type(variant string) string { if variant.contains('.') { return variant } if variant in t.structs { return variant } if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' { qvariant := '${t.cur_module}.${variant}' if qvariant in t.structs { return qvariant } } for name, _ in t.structs { if name.contains('.') && name.all_after_last('.') == variant { return name } } return variant } // smartcast_target_type supports smartcast target type handling for Transformer. fn (t &Transformer) smartcast_target_type(sc SmartcastContext) string { if t.is_interface_type_name(sc.sum_type_name) { return t.interface_variant_type(sc.variant_name) } return t.resolve_variant(sc.sum_type_name, sc.variant_name) } // sum_type_index supports sum type index handling for Transformer. fn (t &Transformer) sum_type_index(sum_name string, variant string) int { resolved_sum := t.resolve_sum_name(sum_name) variants := t.sum_types[resolved_sum] or { if !isnil(t.tc) { tc_variants := t.tc.sum_types[resolved_sum] or { return 0 } return sum_type_index_in_variants(tc_variants, variant) } return 0 } return sum_type_index_in_variants(variants, variant) } // sum_type_index_in_variants supports sum type index in variants handling for transform. fn sum_type_index_in_variants(variants []string, variant string) int { short_variant := variant_short_name_text(variant) for i, v in variants { short_v := variant_short_name_text(v) if v == variant || short_v == short_variant { return i + 1 } } return 0 } // transform_is_expr transforms transform is expr data for transform. fn (mut t Transformer) transform_is_expr(id flat.NodeId, node flat.Node) flat.NodeId { if node.children_count == 0 { return id } expr_id := t.a.child(&node, 0) expr_type := t.node_type(expr_id) clean_type0 := t.trim_pointer_type(expr_type) clean_type := if clean_type0 in t.sum_types { clean_type0 } else { t.find_sum_type_for_variant(node.value) } if t.is_interface_type_name(clean_type0) { new_expr := t.transform_expr(expr_id) object := t.make_selector_op(new_expr, '_object', 'voidptr', if expr_type.starts_with('&') { .arrow } else { .dot }) return t.make_infix(.ne, object, t.a.add(.nil_literal)) } if clean_type.len == 0 || clean_type !in t.sum_types { return t.make_bool_literal(true) } new_expr := t.transform_expr(expr_id) if check := t.make_sum_type_pattern_check(new_expr, expr_type, clean_type, node.value) { return check } return t.make_bool_literal(true) } // make_sum_is_check builds make sum is check data for transform. fn (mut t Transformer) make_sum_is_check(expr flat.NodeId, expr_type string, sum_name string, variant string) flat.NodeId { tag := t.make_selector_op(expr, 'typ', 'int', if expr_type.starts_with('&') { .arrow } else { .dot }) idx := t.make_int_literal(t.sum_type_index(sum_name, variant)) return t.make_infix(.eq, tag, idx) } // sum_variant_path supports sum variant path handling for Transformer. fn (t &Transformer) sum_variant_path(sum_name string, variant string) []string { mut visited := map[string]bool{} return t.sum_variant_path_inner(sum_name, variant, mut visited) } // sum_variant_path_inner supports sum variant path inner handling for Transformer. fn (t &Transformer) sum_variant_path_inner(sum_name string, variant string, mut visited map[string]bool) []string { resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_name)) if resolved_sum.len == 0 || resolved_sum in visited { return []string{} } visited[resolved_sum] = true if direct := t.sum_variant_name(resolved_sum, variant) { return [direct] } variants := t.sum_types[resolved_sum] or { return []string{} } for direct in variants { direct_sum := t.resolve_sum_name(t.trim_pointer_type(direct)) if direct_sum == resolved_sum || direct_sum !in t.sum_types { continue } nested := t.sum_variant_path_inner(direct_sum, variant, mut visited) if nested.len == 0 { continue } mut path := []string{cap: nested.len + 1} path << direct path << nested return path } return []string{} } // make_sum_type_pattern_check builds make sum type pattern check data for transform. fn (mut t Transformer) make_sum_type_pattern_check(expr flat.NodeId, expr_type string, sum_name string, variant string) ?flat.NodeId { resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_name)) path := t.sum_variant_path(resolved_sum, variant) if path.len == 0 { return none } mut current := expr mut current_type := expr_type mut current_sum := resolved_sum mut chain := flat.empty_node for i, path_variant in path { cmp := t.make_sum_is_check(current, current_type, current_sum, path_variant) if int(chain) < 0 { chain = cmp } else { chain = t.make_infix(.logical_and, chain, cmp) } if i == path.len - 1 { break } qv := t.resolve_variant(current_sum, path_variant) use_ptr := t.variant_references_sum(qv, current_sum) field_type := if use_ptr { '&${qv}' } else { qv } current = t.make_selector_op(current, t.sum_field_name(qv), field_type, if current_type.starts_with('&') { .arrow } else { .dot }) current_type = field_type current_sum = t.resolve_sum_name(t.trim_pointer_type(qv)) } return chain } // transform_as_expr converts transform as expr data for transform. fn (mut t Transformer) transform_as_expr(id flat.NodeId, node flat.Node) flat.NodeId { if node.children_count == 0 { return id } expr_id := t.a.child(&node, 0) expr_type := t.node_type(expr_id) clean_type0 := t.trim_pointer_type(expr_type) clean_type := if clean_type0 in t.sum_types { clean_type0 } else { t.find_sum_type_for_variant(node.value) } if clean_type.len == 0 || clean_type !in t.sum_types { return t.transform_expr(expr_id) } qv := t.resolve_variant(clean_type, node.value) if qv.len > 0 && t.normalize_type_alias(clean_type0) == t.normalize_type_alias(qv) { return t.transform_expr(expr_id) } sc_key := t.expr_key(expr_id) if sc_key.len > 0 { contexts := t.smartcasts_for(sc_key) mut matched_contexts := []SmartcastContext{} for i, sc in contexts { mut sc_variant := sc.variant_name if sc.variant_name.contains('.') { sc_variant = sc.variant_name.all_after_last('.') } mut target_variant := node.value if node.value.contains('.') { target_variant = node.value.all_after_last('.') } if sc_variant == target_variant { end := i + 1 matched_contexts = contexts[..end].clone() } } if matched_contexts.len > 0 { base := t.make_plain_expr_for_smartcast(expr_id) return t.apply_smartcast_contexts(base, t.original_expr_type(expr_id), matched_contexts) } } field := t.sum_field_name(qv) new_expr := t.transform_expr(expr_id) use_ptr := t.variant_references_sum(qv, clean_type) field_typ := if use_ptr { '&${qv}' } else { qv } field_sel := t.make_selector_op(new_expr, field, field_typ, if expr_type.starts_with('&') { .arrow } else { .dot }) if use_ptr { return t.make_prefix(.mul, field_sel) } return field_sel } // wrap_sum_return_expr transforms wrap sum return expr data for transform. fn (mut t Transformer) wrap_sum_return_expr(expr_id flat.NodeId) flat.NodeId { was_return_expr := t.in_return_expr t.in_return_expr = true result := if t.cur_fn_ret_type.len == 0 || t.cur_fn_ret_type !in t.sum_types { t.transform_expr(expr_id) } else { t.wrap_sum_value(expr_id, t.cur_fn_ret_type) } t.in_return_expr = was_return_expr return result } // wrap_sum_value transforms wrap sum value data for transform. fn (mut t Transformer) wrap_sum_value(expr_id flat.NodeId, target_sum string) flat.NodeId { resolved_sum := t.resolve_sum_name(target_sum) if resolved_sum.len == 0 || resolved_sum !in t.sum_types { return t.transform_expr(expr_id) } expr := t.a.nodes[int(expr_id)] if expr.kind == .if_expr { branch_type := t.if_expr_branch_result_type(expr) if t.if_expr_branch_overrides_sum_target(branch_type, resolved_sum) { return t.transform_expr(expr_id) } if lowered := t.try_expand_if_expr_value_for_type(expr_id, expr, resolved_sum) { return lowered } } expr_type := t.node_type(expr_id) mut variant := expr_type mut expr_smartcast := SmartcastContext{} key := t.expr_key(expr_id) if key.len > 0 { if sc := t.find_smartcast(key) { expr_smartcast = sc } } has_expr_smartcast := expr_smartcast.expr_name.len > 0 if has_expr_smartcast && t.resolve_sum_name(expr_smartcast.sum_type_name) == resolved_sum { variant = t.resolve_variant(expr_smartcast.sum_type_name, expr_smartcast.variant_name) } if expr.kind == .prefix && expr.op == .mul && expr.children_count > 0 { inner_type := t.node_type(t.a.child(&expr, 0)) if inner_type.starts_with('&') { variant = inner_type[1..] } } if expr.kind == .struct_init || expr.kind == .cast_expr { variant = expr.value } else if expr.kind == .as_expr && expr.value.len > 0 { variant = expr.value } else if expr.kind == .assoc && expr.value.len > 0 { variant = expr.value } else if expr.kind == .assoc && expr.children_count > 0 { variant = t.node_type(t.a.child(&expr, 0)) } if expr.kind !in [.assoc, .as_expr, .prefix] && !has_expr_smartcast && t.resolve_sum_name(expr_type) == resolved_sum { return t.transform_expr(expr_id) } if expr_type.starts_with('&') && t.resolve_sum_name(expr_type[1..]) == resolved_sum { inner := t.transform_expr(expr_id) deref := t.make_prefix(.mul, inner) t.a.nodes[int(deref)].typ = resolved_sum return deref } if variant.len == 0 { return t.transform_expr(expr_id) } mut clean_variant := if variant.starts_with('&') { variant[1..] } else { variant } if clean_variant.starts_with('ptr') && clean_variant.len > 3 && clean_variant[3..].contains('.') { clean_variant = clean_variant[3..] } if clean_variant.starts_with('ptr') && clean_variant.len > 3 && clean_variant[3..].contains('__') { clean_variant = clean_variant[3..].replace('__', '.') } short_variant := t.variant_short_name(clean_variant) mut matches := false mut matched_variant := clean_variant for v in t.sum_types[resolved_sum] { short_v := t.variant_short_name(v) if v == clean_variant || short_v == short_variant { matches = true matched_variant = v break } } if !matches { return t.transform_expr(expr_id) } ref_variant := t.variant_references_sum(matched_variant, resolved_sum) mut pointer_variant_child := flat.empty_node if expr.kind == .prefix && expr.op == .mul && expr.children_count > 0 { pointer_variant_child = t.a.child(&expr, 0) } inner := if int(pointer_variant_child) >= 0 && ref_variant { t.transform_expr(pointer_variant_child) } else { t.transform_expr(expr_id) } if expr_type.starts_with('&') { return t.make_sum_literal(resolved_sum, matched_variant, inner) } if int(pointer_variant_child) >= 0 && ref_variant { return t.make_sum_literal(resolved_sum, matched_variant, inner) } if ref_variant { return t.make_sum_literal(resolved_sum, matched_variant, inner) } start := t.a.children.len t.a.children << inner return t.a.add_node(flat.Node{ kind: .cast_expr value: resolved_sum children_start: start children_count: 1 typ: resolved_sum }) } // ensure_sum_variant_ref supports ensure sum variant ref handling for Transformer. fn (mut t Transformer) ensure_sum_variant_ref(value flat.NodeId, variant string) flat.NodeId { mut value_type := t.node_type(value) if value_type.starts_with('&') { return value } clean_variant := if variant.starts_with('&') { variant[1..] } else { variant } if value_type.len == 0 { value_type = clean_variant } if t.expr_can_take_address(value) || t.a.nodes[int(value)].kind == .struct_init { ref := t.make_prefix(.amp, value) t.a.nodes[int(ref)].typ = '&${clean_variant}' return ref } tmp_name := t.new_temp('sum_val') t.pending_stmts << t.make_decl_assign_typed(tmp_name, value, value_type) ref := t.make_prefix(.amp, t.make_ident(tmp_name)) t.a.nodes[int(ref)].typ = '&${clean_variant}' return ref } // make_sum_literal builds make sum literal data for transform. fn (mut t Transformer) make_sum_literal(sum_name string, variant string, value flat.NodeId) flat.NodeId { qvariant := t.resolve_variant(sum_name, variant) typ_field := t.make_sum_literal_field('typ', t.make_int_literal(t.sum_type_index(sum_name, qvariant)), 'int') raw_value_type := t.node_type(value) value_type := if raw_value_type.starts_with('&') { raw_value_type } else { qvariant } value_field := t.make_sum_literal_field(t.sum_field_name(qvariant), value, value_type) start := t.a.children.len t.a.children << typ_field t.a.children << value_field return t.a.add_node(flat.Node{ kind: .struct_init children_start: start children_count: 2 value: sum_name typ: sum_name }) } // make_sum_literal_field builds make sum literal field data for transform. fn (mut t Transformer) make_sum_literal_field(name string, value flat.NodeId, typ string) flat.NodeId { start := t.a.children.len t.a.children << value return t.a.add_node(flat.Node{ kind: .field_init children_start: start children_count: 1 value: name typ: typ }) }