Files
agila_open_tool/tools/v/vlib/v3/transform/sum.v
T

534 lines
15 KiB
V

module transform
import v3.flat
fn (t &Transformer) trim_pointer_type(typ string) string {
if typ.starts_with('&') {
return typ[1..]
}
return typ
}
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
}
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
}
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
}
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
}
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
}
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
}
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)
}
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)
}
fn sum_type_index_in_variants(variants []string, variant string) int {
short_variant := if variant.contains('.') { variant.all_after_last('.') } else { variant }
for i, v in variants {
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
if v == variant || short_v == short_variant {
return i + 1
}
}
return 0
}
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)
}
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)
}
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)
}
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{}
}
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
}
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
}
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
}
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 := if clean_variant.contains('.') {
clean_variant.all_after_last('.')
} else {
clean_variant
}
mut matches := false
mut matched_variant := clean_variant
for v in t.sum_types[resolved_sum] {
short_v := if v.contains('.') { v.all_after_last('.') } else { 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
})
}
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
}
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
})
}
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
})
}