2430 lines
73 KiB
V
2430 lines
73 KiB
V
module transform
|
|
|
|
import v3.flat
|
|
import v3.types
|
|
|
|
// resolve_call_name resolves the function name from a .call node.
|
|
// child[0] is the function expression: .ident for plain calls, .selector for method calls.
|
|
fn (t &Transformer) resolve_call_name(node flat.Node) string {
|
|
if node.children_count == 0 {
|
|
return ''
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
if int(fn_id) < 0 {
|
|
return ''
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
match fn_node.kind {
|
|
.ident {
|
|
name := fn_node.value
|
|
// Try unqualified name first
|
|
if t.is_known_fn_name(name) {
|
|
return name
|
|
}
|
|
// Try qualified with current module
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
qname := '${t.cur_module}.${name}'
|
|
if t.is_known_fn_name(qname) {
|
|
return qname
|
|
}
|
|
}
|
|
return name
|
|
}
|
|
.selector {
|
|
if fn_node.children_count > 0 {
|
|
base_id := t.a.children[fn_node.children_start]
|
|
base := t.a.nodes[int(base_id)]
|
|
if base.kind == .ident {
|
|
full := '${base.value}.${fn_node.value}'
|
|
if t.is_known_fn_name(full) {
|
|
return full
|
|
}
|
|
}
|
|
method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
|
|
if method_name.len > 0 {
|
|
return method_name
|
|
}
|
|
if base.kind == .ident {
|
|
return '${base.value}.${fn_node.value}'
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
else {
|
|
return ''
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (t &Transformer) is_known_fn_name(name string) bool {
|
|
if name in t.fn_ret_types {
|
|
return true
|
|
}
|
|
if !isnil(t.tc) {
|
|
return name in t.tc.fn_ret_types || name in t.tc.fn_param_types
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (t &Transformer) resolve_receiver_method_name(base_id flat.NodeId, method string) string {
|
|
if method.len == 0 {
|
|
return ''
|
|
}
|
|
mut base_type := t.lvalue_type(base_id)
|
|
if base_type.starts_with('&') {
|
|
base_type = base_type[1..]
|
|
}
|
|
if base_type.len == 0 {
|
|
return ''
|
|
}
|
|
if method_name := t.resolve_receiver_method_for_type(base_type, method) {
|
|
return method_name
|
|
}
|
|
mut raw_var_clean := ''
|
|
if raw_var_type := t.raw_var_type_for_expr(base_id) {
|
|
raw_clean := if raw_var_type.starts_with('&') {
|
|
raw_var_type[1..]
|
|
} else {
|
|
raw_var_type
|
|
}
|
|
raw_var_clean = raw_clean
|
|
if raw_clean.len > 0 && raw_clean != base_type {
|
|
if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
|
|
return method_name
|
|
}
|
|
}
|
|
}
|
|
if raw_const_type := t.raw_const_type_name_for_expr(base_id) {
|
|
raw_clean := if raw_const_type.starts_with('&') {
|
|
raw_const_type[1..]
|
|
} else {
|
|
raw_const_type
|
|
}
|
|
if raw_clean.len > 0 && raw_clean != base_type && raw_clean != raw_var_clean {
|
|
if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
|
|
return method_name
|
|
}
|
|
}
|
|
}
|
|
if alias_method := t.resolve_alias_receiver_method(base_type, method) {
|
|
return alias_method
|
|
}
|
|
return ''
|
|
}
|
|
|
|
fn (t &Transformer) resolve_receiver_method_for_type(receiver_type string, method string) ?string {
|
|
mut clean_type := receiver_type
|
|
if clean_type.starts_with('&') {
|
|
clean_type = clean_type[1..]
|
|
}
|
|
if clean_type.starts_with('map[') {
|
|
for candidate in t.map_receiver_method_candidates(clean_type, method) {
|
|
if t.is_known_fn_name(candidate) {
|
|
return candidate
|
|
}
|
|
}
|
|
} else {
|
|
direct := '${clean_type}.${method}'
|
|
if t.is_known_fn_name(direct) {
|
|
return direct
|
|
}
|
|
}
|
|
if clean_type.starts_with('[]') {
|
|
elem_type := clean_type[2..]
|
|
short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
|
|
short_array := '[]${short_elem}.${method}'
|
|
if t.is_known_fn_name(short_array) {
|
|
return short_array
|
|
}
|
|
if elem_type.contains('.') {
|
|
qualified_array := '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
|
|
if t.is_known_fn_name(qualified_array) {
|
|
return qualified_array
|
|
}
|
|
}
|
|
} else if clean_type.contains('.') {
|
|
short_type := clean_type.all_after_last('.')
|
|
short_method := '${short_type}.${method}'
|
|
if t.is_known_fn_name(short_method) {
|
|
return short_method
|
|
}
|
|
qualified_short_method := '${clean_type.all_before_last('.')}.${short_type}.${method}'
|
|
if t.is_known_fn_name(qualified_short_method) {
|
|
return qualified_short_method
|
|
}
|
|
}
|
|
if !isnil(t.tc) {
|
|
if target := t.tc.type_aliases[clean_type] {
|
|
alias_method := '${target}.${method}'
|
|
if t.is_known_fn_name(alias_method) {
|
|
return alias_method
|
|
}
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) resolve_alias_receiver_method(base_type string, method string) ?string {
|
|
if isnil(t.tc) || base_type.len == 0 || method.len == 0 {
|
|
return none
|
|
}
|
|
clean_base := t.normalize_type_alias(base_type)
|
|
if alias_method := t.alias_methods['${clean_base}.${method}'] {
|
|
return alias_method
|
|
}
|
|
if !t.is_integer_type_name(clean_base) {
|
|
return none
|
|
}
|
|
for name, params in t.tc.fn_param_types {
|
|
if !name.ends_with('.${method}') || params.len == 0 {
|
|
continue
|
|
}
|
|
receiver_name := name.all_before_last('.')
|
|
if receiver_name.len == 0 || receiver_name !in t.tc.type_aliases {
|
|
continue
|
|
}
|
|
param_name := params[0].name()
|
|
if t.alias_receiver_type_matches(clean_base, param_name) {
|
|
return name
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) alias_receiver_type_matches(base_type string, alias_type string) bool {
|
|
if base_type.len == 0 || alias_type.len == 0 {
|
|
return false
|
|
}
|
|
clean_alias := if alias_type.starts_with('&') { alias_type[1..] } else { alias_type }
|
|
alias_target := t.normalize_type_alias(clean_alias)
|
|
if alias_target == base_type {
|
|
return true
|
|
}
|
|
if !isnil(t.tc) {
|
|
alias_c_type := t.tc.c_type(t.tc.parse_type(alias_target))
|
|
base_c_type := t.tc.c_type(t.tc.parse_type(base_type))
|
|
if alias_c_type == base_c_type {
|
|
return true
|
|
}
|
|
}
|
|
return t.is_integer_type_name(alias_target) && t.is_integer_type_name(base_type)
|
|
}
|
|
|
|
fn (t &Transformer) is_integer_type_name(typ string) bool {
|
|
return typ in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'rune',
|
|
'isize', 'usize']
|
|
}
|
|
|
|
fn (t &Transformer) raw_var_type_for_expr(id flat.NodeId) ?string {
|
|
if int(id) < 0 {
|
|
return none
|
|
}
|
|
node := t.a.nodes[int(id)]
|
|
if node.kind == .ident {
|
|
typ := t.var_type(node.value)
|
|
if typ.len > 0 {
|
|
return typ
|
|
}
|
|
}
|
|
if node.typ.len > 0 {
|
|
return node.typ
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) raw_const_type_name_for_expr(id flat.NodeId) ?string {
|
|
if int(id) < 0 || isnil(t.tc) {
|
|
return none
|
|
}
|
|
name := t.expr_key(id)
|
|
if name.len == 0 {
|
|
return none
|
|
}
|
|
key := t.const_type_key(name) or { return none }
|
|
typ := t.tc.const_types[key] or { return none }
|
|
return typ.name()
|
|
}
|
|
|
|
// resolve_method_receiver_type determines the receiver type for method calls.
|
|
// For a call where child[0] is a .selector, resolves the type of the selector's base expression.
|
|
fn (t &Transformer) resolve_method_receiver_type(call_node flat.Node) string {
|
|
if call_node.children_count == 0 {
|
|
return ''
|
|
}
|
|
fn_id := t.a.children[call_node.children_start]
|
|
if int(fn_id) < 0 {
|
|
return ''
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return ''
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
return t.resolve_expr_type(base_id)
|
|
}
|
|
|
|
fn (mut t Transformer) normalize_generic_call_expr(id flat.NodeId, node flat.Node) flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return id
|
|
}
|
|
fn_id := t.a.child(&node, 0)
|
|
if int(fn_id) < 0 {
|
|
return id
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {
|
|
return id
|
|
}
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base := t.a.nodes[int(base_id)]
|
|
if base.kind !in [.ident, .selector] {
|
|
return id
|
|
}
|
|
type_arg := t.generic_call_type_arg_name(t.a.child(&fn_node, 1))
|
|
if type_arg.len == 0 {
|
|
return id
|
|
}
|
|
mut children := []flat.NodeId{cap: int(node.children_count)}
|
|
children << base_id
|
|
for i in 1 .. node.children_count {
|
|
children << t.a.child(&node, i)
|
|
}
|
|
start := t.a.children.len
|
|
for child in children {
|
|
t.a.children << child
|
|
}
|
|
return t.a.add_node(flat.Node{
|
|
kind: .call
|
|
op: node.op
|
|
children_start: start
|
|
children_count: flat.child_count(children.len)
|
|
pos: node.pos
|
|
value: type_arg
|
|
typ: node.typ
|
|
})
|
|
}
|
|
|
|
fn (t &Transformer) generic_call_type_arg_name(id flat.NodeId) string {
|
|
if int(id) < 0 {
|
|
return ''
|
|
}
|
|
node := t.a.nodes[int(id)]
|
|
match node.kind {
|
|
.ident {
|
|
return node.value
|
|
}
|
|
.selector {
|
|
if node.children_count == 0 {
|
|
return node.value
|
|
}
|
|
base := t.generic_call_type_arg_name(t.a.child(&node, 0))
|
|
if base.len == 0 {
|
|
return node.value
|
|
}
|
|
return '${base}.${node.value}'
|
|
}
|
|
.prefix {
|
|
if node.children_count == 0 {
|
|
return ''
|
|
}
|
|
child := t.generic_call_type_arg_name(t.a.child(&node, 0))
|
|
if child.len == 0 {
|
|
return ''
|
|
}
|
|
if node.op == .amp {
|
|
return '&${child}'
|
|
}
|
|
return child
|
|
}
|
|
else {
|
|
return ''
|
|
}
|
|
}
|
|
}
|
|
|
|
// transform_call_args transforms all children of a call expression.
|
|
// child[0] is the function expression, children[1..n] are arguments.
|
|
fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return t.a.add_node(flat.Node{
|
|
kind: .call
|
|
op: node.op
|
|
pos: node.pos
|
|
value: node.value
|
|
typ: node.typ
|
|
})
|
|
}
|
|
call_name := t.call_name_for_node(id, node)
|
|
params := t.call_param_types(call_name)
|
|
param_offset := t.call_param_offset(call_name, node, params)
|
|
is_variadic := t.call_is_variadic(call_name)
|
|
variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
|
|
params.len - 1
|
|
} else {
|
|
-1
|
|
}
|
|
mut new_children := []flat.NodeId{cap: int(node.children_count)}
|
|
saved_in_call_callee := t.in_call_callee
|
|
t.in_call_callee = true
|
|
new_children << t.transform_expr(t.a.children[node.children_start])
|
|
t.in_call_callee = saved_in_call_callee
|
|
mut i := 1
|
|
for i < node.children_count {
|
|
arg_idx := new_children.len - 1
|
|
param_idx := arg_idx + param_offset
|
|
arg_id := t.a.child(&node, i)
|
|
arg_node := t.a.nodes[int(arg_id)]
|
|
param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
|
|
if arg_node.kind == .field_init {
|
|
if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
|
|
new_children << packed_arg
|
|
i = t.next_non_field_init_arg(node, i)
|
|
continue
|
|
}
|
|
}
|
|
if variadic_idx >= 0 && param_idx == variadic_idx {
|
|
variadic_type := params[variadic_idx]
|
|
if variadic_type is types.Array {
|
|
remaining := int(node.children_count) - i
|
|
if remaining == 1 {
|
|
arg_type := t.node_type(arg_id)
|
|
if arg_type.starts_with('[]') {
|
|
new_children << t.transform_call_arg_for_param(arg_id, param_type)
|
|
} else {
|
|
new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
|
|
}
|
|
} else {
|
|
new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
new_children << t.transform_call_arg_for_param(arg_id, param_type)
|
|
i++
|
|
}
|
|
explicit_args := int(node.children_count) - 1
|
|
if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
|
|
variadic_type := params[variadic_idx]
|
|
if variadic_type is types.Array {
|
|
new_children << t.pack_variadic_args(node, int(node.children_count),
|
|
variadic_type.elem_type)
|
|
}
|
|
}
|
|
start := t.a.children.len
|
|
for nc in new_children {
|
|
t.a.children << nc
|
|
}
|
|
return t.a.add_node(flat.Node{
|
|
kind: .call
|
|
op: node.op
|
|
children_start: start
|
|
children_count: flat.child_count(new_children.len)
|
|
pos: node.pos
|
|
value: node.value
|
|
typ: node.typ
|
|
})
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_join_path_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
|
|
call_name := t.call_name_for_node(id, node)
|
|
if call_name != 'join_path' && call_name != 'os.join_path' {
|
|
return none
|
|
}
|
|
if node.children_count <= 1 {
|
|
return t.make_string_literal('')
|
|
}
|
|
mut result := t.transform_expr(t.a.child(&node, 1))
|
|
for i in 2 .. node.children_count {
|
|
arg := t.transform_expr(t.a.child(&node, i))
|
|
result = t.make_call_typed('join_path_single', arr2(result, arg), 'string')
|
|
}
|
|
return result
|
|
}
|
|
|
|
fn (mut t Transformer) transform_params_struct_call_arg(node flat.Node, field_start int, param_type string) ?flat.NodeId {
|
|
struct_type := t.params_struct_type_name(param_type) or { return none }
|
|
mut field_ids := []flat.NodeId{}
|
|
for i in field_start .. node.children_count {
|
|
field_id := t.a.child(&node, i)
|
|
field := t.a.nodes[int(field_id)]
|
|
if field.kind != .field_init {
|
|
break
|
|
}
|
|
field_ids << field_id
|
|
}
|
|
if field_ids.len == 0 {
|
|
return none
|
|
}
|
|
start := t.a.children.len
|
|
for field_id in field_ids {
|
|
t.a.children << field_id
|
|
}
|
|
struct_id := t.a.add_node(flat.Node{
|
|
kind: .struct_init
|
|
children_start: start
|
|
children_count: flat.child_count(field_ids.len)
|
|
value: struct_type
|
|
typ: struct_type
|
|
})
|
|
return t.transform_struct_fields(struct_id, t.a.nodes[int(struct_id)])
|
|
}
|
|
|
|
fn (t &Transformer) next_non_field_init_arg(node flat.Node, field_start int) int {
|
|
mut i := field_start
|
|
for i < node.children_count {
|
|
field := t.a.child_node(&node, i)
|
|
if field.kind != .field_init {
|
|
break
|
|
}
|
|
i++
|
|
}
|
|
return i
|
|
}
|
|
|
|
fn (t &Transformer) params_struct_type_name(param_type string) ?string {
|
|
if param_type.len == 0 {
|
|
return none
|
|
}
|
|
mut typ := param_type
|
|
if typ.starts_with('&') {
|
|
typ = typ[1..]
|
|
}
|
|
if _ := t.lookup_struct_info(typ) {
|
|
return typ
|
|
}
|
|
normalized := t.normalize_type_alias(typ)
|
|
if normalized != typ {
|
|
if _ := t.lookup_struct_info(normalized) {
|
|
return normalized
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) call_name_for_node(id flat.NodeId, node flat.Node) string {
|
|
if !isnil(t.tc) {
|
|
if name := t.tc.resolved_call_name(id) {
|
|
return name
|
|
}
|
|
}
|
|
return t.resolve_call_name(node)
|
|
}
|
|
|
|
fn (t &Transformer) call_param_offset(call_name string, node flat.Node, params []types.Type) int {
|
|
if params.len == 0 || node.children_count == 0 {
|
|
return 0
|
|
}
|
|
fn_node := t.a.child_node(&node, 0)
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return 0
|
|
}
|
|
base_id := t.a.child(fn_node, 0)
|
|
if t.receiver_method_param_offset(base_id, node, params) == 1 {
|
|
return 1
|
|
}
|
|
method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
|
|
if method_name.len == 0 {
|
|
return 0
|
|
}
|
|
if call_name.len == 0 || call_name == method_name || call_name == c_name(method_name) {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
fn (t &Transformer) call_param_types(call_name string) []types.Type {
|
|
if call_name.len == 0 || isnil(t.tc) {
|
|
return []types.Type{}
|
|
}
|
|
params := t.tc.fn_param_types[call_name] or { return []types.Type{} }
|
|
return params.clone()
|
|
}
|
|
|
|
fn (t &Transformer) call_is_variadic(call_name string) bool {
|
|
if call_name.len == 0 || isnil(t.tc) {
|
|
return false
|
|
}
|
|
return t.tc.fn_variadic[call_name] or { false }
|
|
}
|
|
|
|
fn (t &Transformer) call_param_type_name(call_name string, idx int) string {
|
|
if idx < 0 || call_name.len == 0 || isnil(t.tc) {
|
|
return ''
|
|
}
|
|
params := t.tc.fn_param_types[call_name] or { return '' }
|
|
if idx >= params.len {
|
|
return ''
|
|
}
|
|
return params[idx].name()
|
|
}
|
|
|
|
fn (mut t Transformer) transform_call_arg_for_param(arg_id flat.NodeId, param_type string) flat.NodeId {
|
|
if int(arg_id) < 0 {
|
|
return arg_id
|
|
}
|
|
mut arg_node := &t.a.nodes[int(arg_id)]
|
|
if arg_node.kind == .array_literal && arg_node.typ.len == 0 && param_type.starts_with('[]') {
|
|
arg_node.typ = param_type
|
|
}
|
|
if arg_node.kind == .enum_val && param_type in t.enum_types {
|
|
return t.transform_enum_shorthand(arg_id, *arg_node, param_type)
|
|
}
|
|
if param_type.starts_with('&') && arg_node.kind == .selector
|
|
&& t.selector_chain_has_sum_variant_field(arg_id) {
|
|
value := t.transform_expr(arg_id)
|
|
mut value_type := t.node_type(arg_id)
|
|
if value_type.len == 0 {
|
|
value_type = t.node_type(value)
|
|
}
|
|
stable := t.stable_transformed_expr_for_reuse(value, value_type, 'addr')
|
|
addr := t.make_prefix(.amp, stable)
|
|
t.a.nodes[int(addr)].typ = param_type
|
|
return addr
|
|
}
|
|
if param_type.starts_with('&') && t.is_sum_type_name(param_type[1..]) {
|
|
target_sum := param_type[1..]
|
|
arg_type := t.node_type(arg_id)
|
|
arg_key := t.expr_key(arg_id)
|
|
has_smartcast := t.has_smartcast(arg_key)
|
|
if !has_smartcast
|
|
&& t.resolve_sum_name(t.trim_pointer_type(arg_type)) == t.resolve_sum_name(target_sum) {
|
|
return t.transform_expr(arg_id)
|
|
}
|
|
if arg_node.kind == .prefix && arg_node.op == .amp && arg_node.children_count > 0 {
|
|
inner_id := t.a.child(arg_node, 0)
|
|
inner_type := t.node_type(inner_id)
|
|
if t.resolve_sum_name(t.trim_pointer_type(inner_type)) == t.resolve_sum_name(target_sum) {
|
|
return t.transform_expr(arg_id)
|
|
}
|
|
}
|
|
wrapped := t.wrap_sum_value(arg_id, target_sum)
|
|
tmp_name := t.new_temp('sum_arg')
|
|
t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, target_sum)
|
|
return t.make_prefix(.amp, t.make_ident(tmp_name))
|
|
}
|
|
if t.is_sum_type_name(param_type) {
|
|
return t.wrap_sum_value(arg_id, param_type)
|
|
}
|
|
if param_type.starts_with('[]') {
|
|
arg_type := t.node_type(arg_id)
|
|
if is_fixed_array_type(arg_type) {
|
|
return t.fixed_array_value_to_array(arg_id, arg_type, param_type)
|
|
}
|
|
if const_arg := t.transform_const_array_arg_for_param(arg_id, param_type) {
|
|
return const_arg
|
|
}
|
|
}
|
|
if t.is_fn_pointer_type_name(param_type) && t.is_named_fn_value_arg(arg_id) {
|
|
return t.make_cast(param_type, t.transform_expr(arg_id), param_type)
|
|
}
|
|
return t.transform_expr_for_type(arg_id, param_type)
|
|
}
|
|
|
|
fn (t &Transformer) is_fn_pointer_type_name(type_name string) bool {
|
|
if type_name.len == 0 || isnil(t.tc) {
|
|
return false
|
|
}
|
|
typ := t.tc.parse_type(type_name)
|
|
if typ is types.FnType {
|
|
return true
|
|
}
|
|
if typ is types.Alias {
|
|
return typ.base_type is types.FnType
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (t &Transformer) is_named_fn_value_arg(arg_id flat.NodeId) bool {
|
|
if int(arg_id) < 0 || isnil(t.tc) {
|
|
return false
|
|
}
|
|
node := t.a.nodes[int(arg_id)]
|
|
if node.kind == .ident {
|
|
if _ := t.resolve_fn_value_ident(node.value) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
if node.kind == .selector && node.children_count > 0 {
|
|
key := t.expr_key(arg_id)
|
|
return key in t.tc.fn_ret_types || key in t.tc.fn_param_types
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (mut t Transformer) transform_const_array_arg_for_param(arg_id flat.NodeId, param_type string) ?flat.NodeId {
|
|
expr_id := t.const_expr_for_arg(arg_id) or { return none }
|
|
expr := t.a.nodes[int(expr_id)]
|
|
elem_type := param_type[2..]
|
|
if expr.kind == .array_init && expr.children_count == 0 {
|
|
return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
|
|
}
|
|
if expr.kind != .array_literal {
|
|
return none
|
|
}
|
|
if expr.children_count == 0 {
|
|
return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
|
|
}
|
|
mut values := []flat.NodeId{cap: int(expr.children_count)}
|
|
for i in 0 .. expr.children_count {
|
|
values << t.transform_expr(t.a.child(&expr, i))
|
|
}
|
|
return t.make_array_literal_typed(values, param_type)
|
|
}
|
|
|
|
fn (t &Transformer) const_expr_for_arg(arg_id flat.NodeId) ?flat.NodeId {
|
|
if isnil(t.tc) || int(arg_id) < 0 {
|
|
return none
|
|
}
|
|
node := t.a.nodes[int(arg_id)]
|
|
if node.kind == .ident {
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
if expr_id := t.const_expr_for_name('${t.cur_module}.${node.value}') {
|
|
return expr_id
|
|
}
|
|
}
|
|
return t.const_expr_for_name(node.value)
|
|
}
|
|
if node.kind == .selector && node.children_count > 0 {
|
|
base := t.a.child_node(&node, 0)
|
|
if base.kind == .ident {
|
|
return t.const_expr_for_name('${base.value}.${node.value}')
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) const_expr_for_name(name string) ?flat.NodeId {
|
|
if isnil(t.tc) || name.len == 0 {
|
|
return none
|
|
}
|
|
if expr_id := t.tc.const_exprs[name] {
|
|
return expr_id
|
|
}
|
|
key := t.const_type_key(name) or { return none }
|
|
if expr_id := t.tc.const_exprs[key] {
|
|
return expr_id
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (mut t Transformer) pack_variadic_args(node flat.Node, first_arg int, elem_type types.Type) flat.NodeId {
|
|
expected_enum := elem_type.name()
|
|
array_type := '[]${expected_enum}'
|
|
if t.in_const_init {
|
|
mut values := []flat.NodeId{cap: int(node.children_count) - first_arg}
|
|
for i in first_arg .. node.children_count {
|
|
arg_id := t.a.child(&node, i)
|
|
arg := t.a.nodes[int(arg_id)]
|
|
if arg.kind == .enum_val && expected_enum in t.enum_types {
|
|
values << t.transform_enum_shorthand(arg_id, arg, expected_enum)
|
|
} else if t.is_sum_type_name(expected_enum) {
|
|
values << t.wrap_sum_value(arg_id, expected_enum)
|
|
} else {
|
|
values << t.transform_expr(arg_id)
|
|
}
|
|
}
|
|
return t.make_array_literal_typed(values, array_type)
|
|
}
|
|
tmp_name := t.new_temp('varargs')
|
|
t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(expected_enum,
|
|
t.make_int_literal(0), t.make_int_literal(int(node.children_count) - first_arg)),
|
|
array_type)
|
|
for i in first_arg .. node.children_count {
|
|
arg_id := t.a.child(&node, i)
|
|
arg := t.a.nodes[int(arg_id)]
|
|
value := if arg.kind == .enum_val && expected_enum in t.enum_types {
|
|
t.transform_enum_shorthand(arg_id, arg, expected_enum)
|
|
} else if t.is_sum_type_name(expected_enum) {
|
|
t.wrap_sum_value(arg_id, expected_enum)
|
|
} else {
|
|
t.transform_expr(arg_id)
|
|
}
|
|
value_name := t.new_temp('vararg')
|
|
t.pending_stmts << t.make_decl_assign_typed(value_name, value, expected_enum)
|
|
t.pending_stmts << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
|
|
t.make_ident(tmp_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
|
|
}
|
|
t.set_var_type(tmp_name, array_type)
|
|
return t.make_ident(tmp_name)
|
|
}
|
|
|
|
fn (mut t Transformer) make_array_literal_typed(values []flat.NodeId, typ string) flat.NodeId {
|
|
start := t.a.children.len
|
|
for value in values {
|
|
t.a.children << value
|
|
}
|
|
return t.a.add_node(flat.Node{
|
|
kind: .array_literal
|
|
children_start: start
|
|
children_count: flat.child_count(values.len)
|
|
typ: typ
|
|
})
|
|
}
|
|
|
|
fn (mut t Transformer) stringify_expr(expr_id flat.NodeId) flat.NodeId {
|
|
expr := t.transform_expr(expr_id)
|
|
mut typ := t.node_type(expr)
|
|
if typ.len == 0 {
|
|
typ = t.node_type(expr_id)
|
|
}
|
|
return t.wrap_string_conversion(expr, typ)
|
|
}
|
|
|
|
fn (t &Transformer) reliable_stringify_type(id flat.NodeId) string {
|
|
mut typ := t.node_type(id)
|
|
if typ.len > 0 {
|
|
return typ
|
|
}
|
|
if int(id) >= 0 {
|
|
node := t.a.nodes[int(id)]
|
|
if node.typ.len > 0 {
|
|
return node.typ
|
|
}
|
|
match node.kind {
|
|
.int_literal {
|
|
return 'int'
|
|
}
|
|
.float_literal {
|
|
return 'f64'
|
|
}
|
|
.bool_literal {
|
|
return 'bool'
|
|
}
|
|
.char_literal {
|
|
return 'rune'
|
|
}
|
|
.string_literal, .string_interp {
|
|
return 'string'
|
|
}
|
|
.infix {
|
|
return t.reliable_infix_stringify_type(node)
|
|
}
|
|
.prefix {
|
|
if node.op == .not {
|
|
return 'bool'
|
|
}
|
|
if node.children_count > 0 {
|
|
return t.reliable_stringify_type(t.a.child(&node, 0))
|
|
}
|
|
}
|
|
.paren {
|
|
if node.children_count > 0 {
|
|
return t.reliable_stringify_type(t.a.child(&node, 0))
|
|
}
|
|
}
|
|
.cast_expr {
|
|
if node.value.len > 0 {
|
|
return t.normalize_type_alias(node.value)
|
|
}
|
|
if node.children_count > 0 {
|
|
return t.reliable_stringify_type(t.a.child(&node, 0))
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
fn (t &Transformer) reliable_infix_stringify_type(node flat.Node) string {
|
|
if node.children_count < 2 {
|
|
return ''
|
|
}
|
|
lhs_type := t.reliable_stringify_type(t.a.child(&node, 0))
|
|
rhs_type := t.reliable_stringify_type(t.a.child(&node, 1))
|
|
if lhs_type == 'string' || rhs_type == 'string' {
|
|
return 'string'
|
|
}
|
|
match node.op {
|
|
.eq, .ne, .lt, .gt, .le, .ge, .logical_and, .logical_or {
|
|
return 'bool'
|
|
}
|
|
.plus, .minus, .mul, .div, .mod, .left_shift, .right_shift, .right_shift_unsigned, .amp,
|
|
.pipe, .xor {
|
|
if lhs_type.len > 0 && rhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type)
|
|
&& t.is_numeric_stringify_type(rhs_type) {
|
|
return lhs_type
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
fn (t &Transformer) is_numeric_stringify_type(typ string) bool {
|
|
is_number := typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8', 'byte', 'u16',
|
|
'u32', 'u64', 'f32', 'f64', 'rune']
|
|
return is_number || typ in t.enum_types
|
|
}
|
|
|
|
fn (t &Transformer) is_enum_stringify_type(typ string) bool {
|
|
mut clean_typ := typ
|
|
if clean_typ.starts_with('&') {
|
|
clean_typ = clean_typ[1..]
|
|
}
|
|
if clean_typ.starts_with('mut ') {
|
|
clean_typ = clean_typ[4..]
|
|
}
|
|
if clean_typ in t.enum_types {
|
|
return true
|
|
}
|
|
mut qtyp := clean_typ
|
|
if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
|
&& t.cur_module != 'builtin' {
|
|
qtyp = '${t.cur_module}.${clean_typ}'
|
|
if qtyp in t.enum_types {
|
|
return true
|
|
}
|
|
}
|
|
if isnil(t.tc) {
|
|
return false
|
|
}
|
|
if alias := t.tc.type_aliases[clean_typ] {
|
|
return t.is_enum_stringify_type(alias)
|
|
}
|
|
if qtyp != clean_typ {
|
|
if alias := t.tc.type_aliases[qtyp] {
|
|
return t.is_enum_stringify_type(alias)
|
|
}
|
|
}
|
|
parsed := t.tc.parse_type(clean_typ)
|
|
if parsed is types.Enum {
|
|
return true
|
|
}
|
|
if qtyp != clean_typ {
|
|
qparsed := t.tc.parse_type(qtyp)
|
|
if qparsed is types.Enum {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (t &Transformer) enum_str_method_name(typ string) ?string {
|
|
mut candidates := []string{cap: 3}
|
|
candidates << typ
|
|
if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
|
&& t.cur_module != 'builtin' {
|
|
candidates << '${t.cur_module}.${typ}'
|
|
}
|
|
if !isnil(t.tc) {
|
|
parsed := t.tc.parse_type(typ)
|
|
if parsed is types.Enum {
|
|
candidates << parsed.name
|
|
}
|
|
}
|
|
for candidate in candidates {
|
|
method := '${candidate}.str'
|
|
if t.is_known_fn_name(method) {
|
|
return method
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
|
|
mut clean_typ := typ
|
|
is_ref := clean_typ.starts_with('&')
|
|
if clean_typ.starts_with('&') {
|
|
clean_typ = clean_typ[1..]
|
|
}
|
|
if clean_typ.starts_with('builtin.') {
|
|
clean_typ = clean_typ.all_after_last('.')
|
|
}
|
|
if t.is_optional_type_name(clean_typ) {
|
|
return t.wrap_optional_string_conversion(expr, clean_typ)
|
|
}
|
|
if clean_typ.len == 0 || clean_typ == 'unknown' {
|
|
inferred := t.resolve_expr_type(expr)
|
|
if inferred.len > 0 && inferred != clean_typ {
|
|
return t.wrap_string_conversion(expr, inferred)
|
|
}
|
|
}
|
|
if !isnil(t.tc) {
|
|
if alias := t.tc.type_aliases[clean_typ] {
|
|
return t.wrap_string_conversion(expr, alias)
|
|
}
|
|
mut qtyp := clean_typ
|
|
if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
|
&& t.cur_module != 'builtin' {
|
|
qtyp = '${t.cur_module}.${clean_typ}'
|
|
}
|
|
if alias := t.tc.type_aliases[qtyp] {
|
|
return t.wrap_string_conversion(expr, alias)
|
|
}
|
|
if !clean_typ.contains('.') {
|
|
for aname, target in t.tc.type_aliases {
|
|
if aname.all_after_last('.') == clean_typ {
|
|
return t.wrap_string_conversion(expr, target)
|
|
}
|
|
}
|
|
}
|
|
parsed := t.tc.parse_type(clean_typ)
|
|
if parsed is types.Enum {
|
|
if method := t.enum_str_method_name(clean_typ) {
|
|
return t.make_call_typed(method, arr1(expr), 'string')
|
|
}
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
if qtyp != clean_typ {
|
|
qparsed := t.tc.parse_type(qtyp)
|
|
if qparsed is types.Enum {
|
|
if method := t.enum_str_method_name(qtyp) {
|
|
return t.make_call_typed(method, arr1(expr), 'string')
|
|
}
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
}
|
|
}
|
|
if clean_typ == 'string' {
|
|
return expr
|
|
}
|
|
if is_ref || clean_typ in ['voidptr', 'byteptr', 'charptr'] {
|
|
return t.make_call_typed('ptr_str', arr1(expr), 'string')
|
|
}
|
|
if clean_typ == 'IError' || clean_typ.ends_with('.IError') {
|
|
start := t.a.children.len
|
|
t.a.children << expr
|
|
return t.a.add_node(flat.Node{
|
|
kind: .selector
|
|
op: .dot
|
|
children_start: start
|
|
children_count: 1
|
|
value: 'message'
|
|
typ: 'string'
|
|
})
|
|
}
|
|
match clean_typ {
|
|
'bool' {
|
|
return t.make_call_typed('bool.str', arr1(expr), 'string')
|
|
}
|
|
'u8', 'byte', 'u16', 'u32', 'u64' {
|
|
return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
'int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize' {
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
'f32' {
|
|
return t.make_call_typed('strconv__f32_to_str_l', arr1(expr), 'string')
|
|
}
|
|
'f64' {
|
|
return t.make_call_typed('strconv__f64_to_str_l', arr1(expr), 'string')
|
|
}
|
|
else {
|
|
if clean_typ in t.enum_types {
|
|
if method := t.enum_str_method_name(clean_typ) {
|
|
return t.make_call_typed(method, arr1(expr), 'string')
|
|
}
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
mut qenum := clean_typ
|
|
if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
|
&& t.cur_module != 'builtin' {
|
|
qenum = '${t.cur_module}.${clean_typ}'
|
|
}
|
|
if qenum in t.enum_types {
|
|
if method := t.enum_str_method_name(qenum) {
|
|
return t.make_call_typed(method, arr1(expr), 'string')
|
|
}
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
}
|
|
if clean_typ in t.structs || clean_typ in t.sum_types {
|
|
mut qualified := clean_typ
|
|
if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
|
|
&& t.cur_module != 'builtin' {
|
|
q := '${t.cur_module}.${clean_typ}'
|
|
if q in t.structs || q in t.sum_types {
|
|
qualified = q
|
|
}
|
|
}
|
|
str_fn := '${c_name(qualified)}__str'
|
|
known_str := str_fn in t.fn_ret_types
|
|
|| (!isnil(t.tc) && str_fn in t.tc.fn_ret_types)
|
|
if known_str {
|
|
return t.make_call_typed(str_fn, arr1(expr), 'string')
|
|
}
|
|
return t.make_string_literal('${qualified}{}')
|
|
} else if is_fixed_array_type(clean_typ) {
|
|
elem_type := fixed_array_elem_type(clean_typ)
|
|
arr := t.fixed_array_value_to_array(expr, clean_typ, '[]${elem_type}')
|
|
return t.wrap_string_conversion(arr, '[]${elem_type}')
|
|
} else if clean_typ.len > 0 && clean_typ.starts_with('[]') {
|
|
return t.make_call_typed('Array_str', arr1(expr), 'string')
|
|
} else if clean_typ == 'rune' {
|
|
return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
|
|
'string')
|
|
} else {
|
|
return expr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut t Transformer) wrap_optional_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
|
|
opt_type := t.qualify_optional_type(typ)
|
|
mut value_type := t.optional_base_type(opt_type)
|
|
if value_type.len == 0 || value_type == 'void' {
|
|
value_type = 'int'
|
|
}
|
|
opt_name := t.new_temp('opt_str')
|
|
res_name := t.new_temp('opt_str_text')
|
|
t.pending_stmts << t.make_decl_assign_typed(opt_name, expr, opt_type)
|
|
t.pending_stmts << t.make_decl_assign_typed(res_name, t.make_string_literal('Option(none)'),
|
|
'string')
|
|
value := t.make_selector(t.make_ident(opt_name), 'value', value_type)
|
|
mut value_str := t.wrap_string_conversion(value, value_type)
|
|
if value_type == 'string' {
|
|
value_str = t.string_plus(t.string_plus(t.make_string_literal("'"), value_str),
|
|
t.make_string_literal("'"))
|
|
}
|
|
some_str := t.string_plus(t.string_plus(t.make_string_literal('Option('), value_str),
|
|
t.make_string_literal(')'))
|
|
assign_some := t.make_assign(t.make_ident(res_name), some_str)
|
|
t.pending_stmts << t.make_if(t.make_selector(t.make_ident(opt_name), 'ok', 'bool'),
|
|
t.make_block(arr1(assign_some)), t.make_empty())
|
|
return t.make_ident(res_name)
|
|
}
|
|
|
|
fn (mut t Transformer) string_plus(left flat.NodeId, right flat.NodeId) flat.NodeId {
|
|
return t.make_call_typed('string__plus', arr2(left, right), 'string')
|
|
}
|
|
|
|
fn (t &Transformer) is_flag_enum_type(typ string) bool {
|
|
mut clean := typ
|
|
if clean.starts_with('&') {
|
|
clean = clean[1..]
|
|
}
|
|
if clean.len == 0 {
|
|
return false
|
|
}
|
|
if !isnil(t.tc) {
|
|
parsed := t.tc.parse_type(clean)
|
|
if parsed is types.Enum {
|
|
return parsed.is_flag
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (t &Transformer) is_runtime_array_flags_selector(id flat.NodeId) bool {
|
|
if int(id) < 0 {
|
|
return false
|
|
}
|
|
node := t.a.nodes[int(id)]
|
|
if node.kind != .selector || node.value != 'flags' || node.children_count == 0 {
|
|
return false
|
|
}
|
|
owner_id := t.a.child(&node, 0)
|
|
owner_type := t.node_type(owner_id).trim_left('&')
|
|
return owner_type.starts_with('[]') || owner_type == 'strings.Builder'
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_flag_enum_stmt(call_id flat.NodeId) ?flat.NodeId {
|
|
if int(call_id) < 0 {
|
|
return none
|
|
}
|
|
call := t.a.nodes[int(call_id)]
|
|
if call.kind != .call || call.children_count < 2 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[call.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0
|
|
|| fn_node.value !in ['set', 'clear'] {
|
|
return none
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
if t.is_runtime_array_flags_selector(base_id) {
|
|
return none
|
|
}
|
|
base_type := t.node_type(base_id)
|
|
if !t.is_flag_enum_type(base_type) {
|
|
return none
|
|
}
|
|
base := t.transform_expr(base_id)
|
|
arg := t.transform_expr(t.a.children[call.children_start + 1])
|
|
if fn_node.value == 'set' {
|
|
return t.make_assign_op(base, arg, .pipe_assign)
|
|
}
|
|
return t.make_assign_op(base, t.make_prefix(.bit_not, arg), .amp_assign)
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_flag_enum_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 1 {
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'zero' {
|
|
return none
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
base_node := t.a.nodes[int(base_id)]
|
|
if base_node.kind != .ident || !t.is_flag_enum_type(base_node.value) {
|
|
return none
|
|
}
|
|
return t.make_cast(base_node.value, t.make_int_literal(0), base_node.value)
|
|
}
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value !in ['has', 'all'] {
|
|
return none
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
if t.is_runtime_array_flags_selector(base_id) {
|
|
return none
|
|
}
|
|
base_type := t.node_type(base_id)
|
|
if !t.is_flag_enum_type(base_type) {
|
|
return none
|
|
}
|
|
base := t.transform_expr(base_id)
|
|
arg_id := t.a.children[node.children_start + 1]
|
|
arg := t.transform_expr(arg_id)
|
|
masked := t.make_infix(.amp, base, arg)
|
|
if fn_node.value == 'has' {
|
|
return t.make_infix(.ne, masked, t.make_int_literal(0))
|
|
}
|
|
arg_copy := t.transform_expr(arg_id)
|
|
return t.make_infix(.eq, masked, arg_copy)
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_array_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return none
|
|
}
|
|
if fn_node.value !in ['clone', 'reverse', 'contains', 'index', 'join', 'any', 'all', 'count',
|
|
'equals', 'prepend', 'insert'] {
|
|
if fn_node.value !in ['filter', 'map', 'sort', 'sorted', 'sort_with_compare',
|
|
'sorted_with_compare'] {
|
|
return none
|
|
}
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
mut base_type := t.node_type(base_id)
|
|
if !base_type.starts_with('[]') && !is_fixed_array_type(base_type) {
|
|
base_node := t.a.nodes[int(base_id)]
|
|
if base_node.kind in [.call, .selector, .as_expr] {
|
|
new_base := t.transform_expr(base_id)
|
|
new_base_type := t.node_type(new_base)
|
|
if new_base_type.starts_with('[]') || is_fixed_array_type(new_base_type) {
|
|
selector := t.make_selector(new_base, fn_node.value, '')
|
|
mut children := []flat.NodeId{cap: int(node.children_count)}
|
|
children << selector
|
|
for i in 1 .. node.children_count {
|
|
children << t.a.child(&node, i)
|
|
}
|
|
start := t.a.children.len
|
|
for child in children {
|
|
t.a.children << child
|
|
}
|
|
new_node := flat.Node{
|
|
kind: .call
|
|
children_start: start
|
|
children_count: node.children_count
|
|
pos: node.pos
|
|
typ: node.typ
|
|
}
|
|
return t.try_lower_array_method_call(new_node)
|
|
}
|
|
}
|
|
}
|
|
clean_base_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
|
if is_fixed_array_type(clean_base_type) {
|
|
elem_type := fixed_array_elem_type(clean_base_type)
|
|
array_type := '[]${elem_type}'
|
|
tmp_name := t.new_temp('fixed_arr')
|
|
t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.fixed_array_value_to_array(base_id,
|
|
clean_base_type, array_type), array_type)
|
|
selector := t.make_selector(t.make_ident(tmp_name), fn_node.value, '')
|
|
mut children := []flat.NodeId{cap: int(node.children_count)}
|
|
children << selector
|
|
for i in 1 .. node.children_count {
|
|
children << t.a.child(&node, i)
|
|
}
|
|
start := t.a.children.len
|
|
for child in children {
|
|
t.a.children << child
|
|
}
|
|
new_node := flat.Node{
|
|
kind: .call
|
|
children_start: start
|
|
children_count: node.children_count
|
|
pos: node.pos
|
|
typ: node.typ
|
|
}
|
|
return t.try_lower_array_method_call(new_node)
|
|
}
|
|
if !clean_base_type.starts_with('[]') {
|
|
return none
|
|
}
|
|
elem_type := clean_base_type[2..]
|
|
if fn_node.value == 'prepend' {
|
|
return t.lower_array_prepend_call(node, fn_node, base_type, elem_type)
|
|
}
|
|
if fn_node.value == 'insert' {
|
|
return t.lower_array_insert_call(node, fn_node, base_type, elem_type)
|
|
}
|
|
if fn_node.value == 'contains' {
|
|
method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
|
|
if method_name.len > 0 {
|
|
args := t.transform_receiver_method_args(node, base_id, method_name)
|
|
ret_type := t.receiver_method_return_type(method_name, node.typ)
|
|
return t.make_call_typed(method_name, args, ret_type)
|
|
}
|
|
}
|
|
match fn_node.value {
|
|
'filter' {
|
|
return t.lower_array_filter_call(node, fn_node, clean_base_type)
|
|
}
|
|
'map' {
|
|
return t.lower_array_map_call(node, fn_node, clean_base_type)
|
|
}
|
|
'sort' {
|
|
return t.lower_array_sort_call(node, fn_node, base_type)
|
|
}
|
|
'sorted' {
|
|
return t.lower_array_sorted_call(node, fn_node, clean_base_type)
|
|
}
|
|
'sort_with_compare' {
|
|
return t.lower_array_sort_with_compare_call(node, fn_node, base_type)
|
|
}
|
|
'sorted_with_compare' {
|
|
return t.lower_array_sorted_with_compare_call(node, fn_node, clean_base_type)
|
|
}
|
|
'any', 'all' {
|
|
return t.lower_array_any_all_call(node, fn_node, clean_base_type, fn_node.value)
|
|
}
|
|
'count' {
|
|
return t.lower_array_count_call(node, fn_node, clean_base_type)
|
|
}
|
|
'equals' {
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
method_name := t.resolve_receiver_method_name(base_id, 'equals')
|
|
if method_name.len > 0 {
|
|
args := t.transform_receiver_method_args(node, base_id, method_name)
|
|
return t.make_call_typed(method_name, args, 'bool')
|
|
}
|
|
receiver := t.transform_expr(base_id)
|
|
arg := t.transform_expr(t.a.children[node.children_start + 1])
|
|
if elem_type == 'string' {
|
|
return t.make_call_typed('array_eq_string', arr2(receiver, arg), 'bool')
|
|
}
|
|
return t.make_call_typed('array_eq_raw', arr3(receiver, arg,
|
|
t.make_sizeof_type(elem_type)), 'bool')
|
|
}
|
|
else {}
|
|
}
|
|
|
|
match fn_node.value {
|
|
'clone' {
|
|
receiver := t.transform_expr(base_id)
|
|
return t.make_call_typed('array_clone', arr1(receiver), clean_base_type)
|
|
}
|
|
'reverse' {
|
|
receiver := t.transform_expr(base_id)
|
|
return t.make_call_typed('array__reverse', arr1(receiver), clean_base_type)
|
|
}
|
|
'contains' {
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
arg_id := t.a.children[node.children_start + 1]
|
|
if lowered := t.lower_array_membership_expr(base_id, arg_id, base_type, true, node) {
|
|
return lowered
|
|
}
|
|
receiver := t.transform_expr(base_id)
|
|
arg := t.transform_expr(arg_id)
|
|
fn_name := if elem_type == 'string' {
|
|
'array_contains_string'
|
|
} else {
|
|
'array_contains_int'
|
|
}
|
|
return t.make_call_typed(fn_name, arr2(receiver, arg), 'bool')
|
|
}
|
|
'index' {
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
arg_id := t.a.children[node.children_start + 1]
|
|
if lowered := t.lower_array_index_expr(base_id, arg_id, base_type, true, node) {
|
|
return lowered
|
|
}
|
|
receiver := t.transform_expr(base_id)
|
|
arg := t.transform_expr(arg_id)
|
|
fn_name := if elem_type == 'string' { 'array_index_string' } else { 'array_index_int' }
|
|
return t.make_call_typed(fn_name, arr2(receiver, arg), 'int')
|
|
}
|
|
'join' {
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
receiver := t.transform_expr(base_id)
|
|
arg := t.transform_expr(t.a.children[node.children_start + 1])
|
|
return t.make_call_typed('array_string_join', arr2(receiver, arg), 'string')
|
|
}
|
|
else {
|
|
return none
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_map_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0
|
|
|| fn_node.value !in ['keys', 'values'] {
|
|
return none
|
|
}
|
|
base_id := t.a.children[fn_node.children_start]
|
|
base_type := t.node_type(base_id)
|
|
clean_type := t.clean_map_type(base_type)
|
|
if !clean_type.starts_with('map[') {
|
|
return none
|
|
}
|
|
elem_type := if fn_node.value == 'keys' {
|
|
t.map_key_type(clean_type)
|
|
} else {
|
|
t.map_value_type(clean_type)
|
|
}
|
|
if elem_type.len == 0 {
|
|
return none
|
|
}
|
|
base := t.transform_expr(base_id)
|
|
return t.make_call_typed('map__${fn_node.value}', arr1(t.runtime_addr(base, base_type)),
|
|
'[]${elem_type}')
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_move_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count != 1 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'move' {
|
|
return none
|
|
}
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base_type := t.node_type(base_id)
|
|
clean_array_type := t.membership_container_type(base_type)
|
|
clean_map_type := t.clean_map_type(base_type)
|
|
if clean_array_type.starts_with('[]') || clean_map_type.starts_with('map[') {
|
|
return t.transform_expr(base_id)
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.NodeId {
|
|
name := t.new_temp('anon_fn')
|
|
mut param_types := []types.Type{}
|
|
mut param_ids := []flat.NodeId{}
|
|
mut body_ids := []flat.NodeId{}
|
|
for i in 0 .. node.children_count {
|
|
child_id := t.a.child(&node, i)
|
|
child := t.a.nodes[int(child_id)]
|
|
if child.kind == .param {
|
|
param_ids << child_id
|
|
if !isnil(t.tc) {
|
|
param_types << t.tc.parse_type(child.typ)
|
|
}
|
|
} else if child.kind != .ident {
|
|
body_ids << child_id
|
|
}
|
|
}
|
|
ret_type := if node.typ.len > 0 { node.typ } else { 'void' }
|
|
saved_fn_name := t.cur_fn_name
|
|
saved_ret_type := t.cur_fn_ret_type
|
|
saved_vars := t.var_types.clone()
|
|
t.cur_fn_name = name
|
|
t.cur_fn_ret_type = ret_type
|
|
t.reset_var_types()
|
|
for param_id in param_ids {
|
|
param := t.a.nodes[int(param_id)]
|
|
if param.value.len > 0 && param.typ.len > 0 {
|
|
t.set_var_type(param.value, param.typ)
|
|
}
|
|
}
|
|
new_body := t.transform_stmts(body_ids)
|
|
t.var_types = saved_vars
|
|
t.cur_fn_name = saved_fn_name
|
|
t.cur_fn_ret_type = saved_ret_type
|
|
mut all_ids := []flat.NodeId{cap: param_ids.len + new_body.len}
|
|
for param_id in param_ids {
|
|
all_ids << param_id
|
|
}
|
|
for body_id in new_body {
|
|
all_ids << body_id
|
|
}
|
|
if t.cur_module.len > 0 {
|
|
t.a.add_node(flat.Node{
|
|
kind: .module_decl
|
|
value: t.cur_module
|
|
})
|
|
}
|
|
start := t.a.children.len
|
|
for child_id in all_ids {
|
|
t.a.children << child_id
|
|
}
|
|
t.a.add_node(flat.Node{
|
|
kind: .fn_decl
|
|
value: name
|
|
typ: ret_type
|
|
children_start: start
|
|
children_count: flat.child_count(all_ids.len)
|
|
})
|
|
if !isnil(t.tc) {
|
|
ret := t.tc.parse_type(ret_type)
|
|
t.tc.fn_ret_types[name] = ret
|
|
t.tc.fn_param_types[name] = param_types.clone()
|
|
t.tc.fn_variadic[name] = false
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
qname := '${t.cur_module}.${name}'
|
|
t.tc.fn_ret_types[qname] = ret
|
|
t.tc.fn_param_types[qname] = param_types.clone()
|
|
t.tc.fn_variadic[qname] = false
|
|
}
|
|
}
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
return t.make_ident('${t.cur_module}.${name}')
|
|
}
|
|
return t.make_ident(name)
|
|
}
|
|
|
|
// try_lower_builtin_call checks if a call is to a builtin that needs special lowering.
|
|
// Returns none for most calls so the caller falls through to generic call transform.
|
|
fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return none
|
|
}
|
|
if cast_call := t.try_lower_primitive_cast_call(node) {
|
|
return cast_call
|
|
}
|
|
if flag_call := t.try_lower_flag_enum_call(node) {
|
|
return flag_call
|
|
}
|
|
if flag_default := t.try_lower_flag_default_value_call(node) {
|
|
return flag_default
|
|
}
|
|
if pool_call := t.try_lower_pool_generic_method_call(node) {
|
|
return pool_call
|
|
}
|
|
if move_call := t.try_lower_move_method_call(node) {
|
|
return move_call
|
|
}
|
|
if map_call := t.try_lower_map_method_call(node) {
|
|
return map_call
|
|
}
|
|
if array_call := t.try_lower_array_method_call(node) {
|
|
return array_call
|
|
}
|
|
if type_name_call := t.try_lower_sum_type_name_method_call(node) {
|
|
return type_name_call
|
|
}
|
|
if receiver_call := t.try_lower_receiver_method_call(_id, node) {
|
|
return receiver_call
|
|
}
|
|
if string_call := t.try_lower_string_method_call(node) {
|
|
return string_call
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
if int(fn_id) < 0 {
|
|
return none
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .ident {
|
|
return none
|
|
}
|
|
name := fn_node.value
|
|
match name {
|
|
'println', 'eprintln', 'print', 'eprint' {
|
|
if node.children_count < 2 {
|
|
return t.transform_call_args(_id, node)
|
|
}
|
|
arg := t.stringify_expr(t.a.child(&node, 1))
|
|
return t.make_call(name, arr1(arg))
|
|
}
|
|
'panic' {
|
|
if node.children_count == 2 {
|
|
arg_id := t.a.child(&node, 1)
|
|
arg_type := t.node_type(arg_id)
|
|
if arg_type == 'IError' {
|
|
arg := t.transform_expr(arg_id)
|
|
return t.make_call('panic',
|
|
arr1(t.make_method_call(arg, 'str', []flat.NodeId{})))
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
'sizeof' {
|
|
return none
|
|
}
|
|
'typeof' {
|
|
return none
|
|
}
|
|
else {
|
|
return none
|
|
}
|
|
}
|
|
}
|
|
|
|
const primitive_cast_type_names = ['bool', 'int', 'i8', 'i16', 'i32', 'i64', 'isize', 'u8', 'byte',
|
|
'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'rune', 'char']
|
|
|
|
fn (mut t Transformer) try_lower_primitive_cast_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count != 2 {
|
|
return none
|
|
}
|
|
fn_id := t.a.child(&node, 0)
|
|
if int(fn_id) < 0 {
|
|
return none
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .ident || fn_node.value !in primitive_cast_type_names {
|
|
return none
|
|
}
|
|
arg_id := t.a.child(&node, 1)
|
|
return t.make_cast(fn_node.value, t.transform_expr(arg_id), fn_node.value)
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_flag_default_value_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count != 2 {
|
|
return none
|
|
}
|
|
fn_id := t.a.child(&node, 0)
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
mut name := ''
|
|
if fn_node.kind == .ident {
|
|
name = fn_node.value
|
|
} else if fn_node.kind == .selector {
|
|
name = fn_node.value
|
|
}
|
|
if name != 'flag_default_value' {
|
|
return none
|
|
}
|
|
arg_id := t.a.child(&node, 1)
|
|
arg := t.transform_expr(arg_id)
|
|
mut arg_type := t.resolve_expr_type(arg_id)
|
|
if arg_type.len == 0 {
|
|
arg_type = t.reliable_stringify_type(arg_id)
|
|
}
|
|
if arg_type.len == 0 {
|
|
arg_type = t.node_type(arg)
|
|
}
|
|
if t.normalize_type_alias(arg_type) == 'string' {
|
|
escaped := t.make_call_typed('escape_default_string', arr1(arg), 'string')
|
|
return t.string_plus(t.string_plus(t.make_string_literal('"'), escaped),
|
|
t.make_string_literal('"'))
|
|
}
|
|
return t.wrap_string_conversion(arg, arg_type)
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_sum_type_name_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count != 1 {
|
|
return none
|
|
}
|
|
fn_id := t.a.child(&node, 0)
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.value != 'type_name' || fn_node.children_count == 0 {
|
|
return none
|
|
}
|
|
base_id := t.a.child(&fn_node, 0)
|
|
mut base_type := t.node_type(base_id)
|
|
if base_type.len == 0 {
|
|
base_type = t.lvalue_type(base_id)
|
|
}
|
|
clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
|
resolved_sum := t.resolve_sum_name(clean_type)
|
|
variants := t.sum_types[resolved_sum] or { return none }
|
|
base := t.stable_transformed_expr_for_reuse(t.transform_expr(base_id), base_type, 'sum_type')
|
|
tag := t.make_selector_op(base, 'typ', 'int', if base_type.starts_with('&') {
|
|
.arrow
|
|
} else {
|
|
.dot
|
|
})
|
|
return t.build_sum_type_name_chain(tag, resolved_sum, variants, 0)
|
|
}
|
|
|
|
fn (mut t Transformer) build_sum_type_name_chain(tag flat.NodeId, sum_name string, variants []string, idx int) flat.NodeId {
|
|
if idx >= variants.len {
|
|
return t.make_string_literal('')
|
|
}
|
|
variant := variants[idx]
|
|
display := if variant.contains('.') { variant.all_after_last('.') } else { variant }
|
|
cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(sum_name, variant)))
|
|
then_block := t.make_block(arr1(t.make_expr_stmt(t.make_string_literal(display))))
|
|
else_expr := t.build_sum_type_name_chain(tag, sum_name, variants, idx + 1)
|
|
else_block := t.make_block(arr1(t.make_expr_stmt(else_expr)))
|
|
start := t.a.children.len
|
|
t.a.children << cond
|
|
t.a.children << then_block
|
|
t.a.children << else_block
|
|
return t.a.add_node(flat.Node{
|
|
kind: .if_expr
|
|
children_start: start
|
|
children_count: 3
|
|
typ: 'string'
|
|
})
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_pool_generic_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 || node.value.len == 0 {
|
|
return none
|
|
}
|
|
fn_id := t.a.child(&node, 0)
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return none
|
|
}
|
|
method := fn_node.value
|
|
if method !in ['get_item', 'get_results', 'get_results_ref'] {
|
|
return none
|
|
}
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base_type := t.lvalue_type(base_id)
|
|
if !is_pool_processor_type(base_type) {
|
|
return none
|
|
}
|
|
elem_type := node.value
|
|
base := t.stable_expr_for_reuse(base_id)
|
|
mut prefix := []flat.NodeId{}
|
|
t.drain_pending(mut prefix)
|
|
if method == 'get_item' {
|
|
if node.children_count < 2 {
|
|
return none
|
|
}
|
|
idx := t.transform_expr(t.a.child(&node, 1))
|
|
t.drain_pending(mut prefix)
|
|
items := t.pool_processor_field(base, base_type, 'items', '[]voidptr')
|
|
item := t.make_index(items, idx, 'voidptr')
|
|
cast := t.make_cast('&${elem_type}', item, '&${elem_type}')
|
|
value := t.make_prefix(.mul, cast)
|
|
t.a.nodes[int(value)].typ = elem_type
|
|
for stmt in prefix {
|
|
t.pending_stmts << stmt
|
|
}
|
|
return value
|
|
}
|
|
result_name := t.new_temp('pool_results')
|
|
idx_name := t.new_temp('pool_idx')
|
|
results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
|
|
results_len := t.make_selector(results, 'len', 'int')
|
|
is_ref_results := method == 'get_results_ref'
|
|
out_elem_type := if is_ref_results { '&${elem_type}' } else { elem_type }
|
|
out_type := '[]${out_elem_type}'
|
|
prefix << t.make_decl_assign_typed(result_name, t.make_array_new_call(out_elem_type,
|
|
t.make_int_literal(0), results_len), out_type)
|
|
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
|
cond_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
|
|
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(cond_results, 'len', 'int'))
|
|
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
|
body_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
|
|
item := t.make_index(body_results, t.make_ident(idx_name), 'voidptr')
|
|
value := if is_ref_results {
|
|
t.make_cast('&${elem_type}', item, '&${elem_type}')
|
|
} else {
|
|
t.make_prefix(.mul, t.make_cast('&${elem_type}', item, '&${elem_type}'))
|
|
}
|
|
t.a.nodes[int(value)].typ = out_elem_type
|
|
value_name := t.new_temp('pool_result')
|
|
value_decl := t.make_decl_assign_typed(value_name, value, out_elem_type)
|
|
push_call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(result_name)), t.make_prefix(.amp,
|
|
t.make_ident(value_name))), 'void')
|
|
loop_body := [value_decl, t.make_expr_stmt(push_call)]
|
|
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
|
for stmt in prefix {
|
|
t.pending_stmts << stmt
|
|
}
|
|
return t.make_ident(result_name)
|
|
}
|
|
|
|
fn is_pool_processor_type(typ string) bool {
|
|
mut clean := typ
|
|
if clean.starts_with('&') {
|
|
clean = clean[1..]
|
|
}
|
|
if clean.starts_with('mut ') {
|
|
clean = clean[4..]
|
|
}
|
|
return clean == 'PoolProcessor' || clean.ends_with('.PoolProcessor')
|
|
}
|
|
|
|
fn (mut t Transformer) pool_processor_field(base flat.NodeId, base_type string, field string, typ string) flat.NodeId {
|
|
op := if base_type.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
|
|
return t.make_selector_op(base, field, typ, op)
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return none
|
|
}
|
|
method := fn_node.value
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base_node := t.a.nodes[int(base_id)]
|
|
if base_node.kind == .ident {
|
|
static_name := '${base_node.value}.${method}'
|
|
if t.is_known_fn_name(static_name) {
|
|
return none
|
|
}
|
|
}
|
|
mut base_type := if base_node.kind in [.selector, .index] {
|
|
t.lvalue_type(base_id)
|
|
} else {
|
|
t.node_type(base_id)
|
|
}
|
|
if base_type.len == 0 {
|
|
base_type = t.lvalue_type(base_id)
|
|
}
|
|
if base_type.starts_with('&') {
|
|
base_type = base_type[1..]
|
|
}
|
|
if base_type.len == 0 {
|
|
return none
|
|
}
|
|
mut builtin_base_type := t.normalize_type_alias(base_type)
|
|
if builtin_base_type == 'byte' {
|
|
builtin_base_type = 'u8'
|
|
}
|
|
if base_type == '[]rune' && method == 'string' {
|
|
return t.make_call_typed('Array_rune__string', arr1(t.transform_expr(base_id)), 'string')
|
|
}
|
|
if method == 'str' && t.is_builder_receiver(base_id, base_type) {
|
|
method_name := 'strings.Builder.str'
|
|
args := t.transform_receiver_method_args(node, base_id, method_name)
|
|
return t.make_call_typed(method_name, args, 'string')
|
|
}
|
|
if base_type.starts_with('[]') && method == 'str' {
|
|
return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
|
|
}
|
|
if method == 'str' {
|
|
if t.is_enum_stringify_type(base_type) {
|
|
return none
|
|
}
|
|
return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
|
|
}
|
|
if base_type == '[]u8' || base_type == '[]byte' {
|
|
if method == 'bytestr' {
|
|
return t.make_call_typed('Array_u8__bytestr', arr1(t.transform_expr(base_id)), 'string')
|
|
}
|
|
if method == 'hex' {
|
|
return t.make_call_typed('Array_u8__hex', arr1(t.transform_expr(base_id)), 'string')
|
|
}
|
|
}
|
|
if t.is_builder_receiver(base_id, base_type) {
|
|
for method_name in ['strings.Builder.${method}', 'Builder.${method}'] {
|
|
if t.is_known_fn_name(method_name) {
|
|
args := t.transform_receiver_method_args(node, base_id, method_name)
|
|
ret_type := t.receiver_method_return_type(method_name, node.typ)
|
|
return t.make_call_typed(method_name, args, ret_type)
|
|
}
|
|
}
|
|
}
|
|
if builtin_base_type == 'u8' && method in ['is_space', 'is_digit', 'is_hex_digit', 'is_letter'] {
|
|
return t.make_call_typed('u8__${method}', arr1(t.transform_expr(base_id)), 'bool')
|
|
}
|
|
if builtin_base_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'int', 'u64', 'i64', 'rune']
|
|
&& method in ['hex', 'hex_full'] {
|
|
return t.make_call_typed('${builtin_base_type}__${method}',
|
|
arr1(t.transform_expr(base_id)), 'string')
|
|
}
|
|
if base_type.starts_with('[]') || base_type.starts_with('map[') {
|
|
if base_type.starts_with('[]') {
|
|
return none
|
|
}
|
|
}
|
|
if !isnil(t.tc) {
|
|
if resolved_method := t.tc.resolved_call_name(id) {
|
|
if t.is_known_fn_name(resolved_method) {
|
|
params := t.call_param_types(resolved_method)
|
|
if !t.resolved_call_uses_receiver_type(base_id, base_type, params) {
|
|
return none
|
|
}
|
|
args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
|
|
resolved_method), resolved_method)
|
|
ret_type := t.receiver_method_return_type(resolved_method, node.typ)
|
|
return t.make_call_typed(resolved_method, args, ret_type)
|
|
}
|
|
}
|
|
}
|
|
if sum_method := t.resolve_smartcast_sum_receiver_method(base_id, method) {
|
|
args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
|
|
sum_method), sum_method)
|
|
ret_type := t.receiver_method_return_type(sum_method, node.typ)
|
|
return t.make_call_typed(sum_method, args, ret_type)
|
|
}
|
|
method_name := t.resolve_receiver_method_name(base_id, method)
|
|
if method_name.len > 0 {
|
|
args := t.transform_receiver_method_args(node, base_id, method_name)
|
|
ret_type := t.receiver_method_return_type(method_name, node.typ)
|
|
return t.make_call_typed(method_name, args, ret_type)
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) is_builder_receiver(base_id flat.NodeId, base_type string) bool {
|
|
if is_builder_type_name(base_type) {
|
|
return true
|
|
}
|
|
if raw_type := t.raw_var_type_for_expr(base_id) {
|
|
return is_builder_type_name(raw_type)
|
|
}
|
|
if raw_field_type := t.raw_selector_field_type(base_id) {
|
|
return is_builder_type_name(raw_field_type)
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (t &Transformer) raw_selector_field_type(id flat.NodeId) ?string {
|
|
if int(id) < 0 {
|
|
return none
|
|
}
|
|
node := t.a.nodes[int(id)]
|
|
if node.kind != .selector || node.children_count == 0 {
|
|
return none
|
|
}
|
|
base_id := t.a.child(&node, 0)
|
|
mut base_type := t.original_expr_type(base_id)
|
|
if base_type.len == 0 {
|
|
base_type = t.node_type(base_id)
|
|
}
|
|
if base_type.starts_with('&') {
|
|
base_type = base_type[1..]
|
|
}
|
|
return t.lookup_struct_field_raw_type(base_type, node.value)
|
|
}
|
|
|
|
fn is_builder_type_name(typ string) bool {
|
|
mut clean := typ
|
|
if clean.starts_with('&') {
|
|
clean = clean[1..]
|
|
}
|
|
return clean == 'strings.Builder' || clean == 'Builder'
|
|
}
|
|
|
|
fn (t &Transformer) resolved_call_uses_receiver_type(base_id flat.NodeId, receiver_type string, params []types.Type) bool {
|
|
if params.len == 0 {
|
|
return false
|
|
}
|
|
mut param_type := params[0].name()
|
|
if param_type.starts_with('&') {
|
|
param_type = param_type[1..]
|
|
}
|
|
if sc := t.find_smartcast(t.expr_key(base_id)) {
|
|
sc_type := t.trim_pointer_type(t.smartcast_target_type(sc))
|
|
if sc_type.len > 0 && t.normalize_type_alias(sc_type) == t.normalize_type_alias(param_type) {
|
|
return true
|
|
}
|
|
sc_sum := t.trim_pointer_type(t.resolve_sum_name(sc.sum_type_name))
|
|
if sc_sum.len > 0 && t.normalize_type_alias(sc_sum) == t.normalize_type_alias(param_type) {
|
|
return true
|
|
}
|
|
if t.is_sum_type_name(param_type) {
|
|
for parent in t.sum_type_parents_for_variant(sc.variant_name) {
|
|
if t.normalize_type_alias(parent) == t.normalize_type_alias(param_type) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mut base_type := receiver_type
|
|
if base_type.starts_with('&') {
|
|
base_type = base_type[1..]
|
|
}
|
|
if base_type.len == 0 {
|
|
return true
|
|
}
|
|
return t.normalize_type_alias(base_type) == t.normalize_type_alias(param_type)
|
|
}
|
|
|
|
fn (mut t Transformer) receiver_base_for_resolved_method(base_id flat.NodeId, method_name string) flat.NodeId {
|
|
key := t.expr_key(base_id)
|
|
sc := t.find_smartcast(key) or { return t.transform_expr(base_id) }
|
|
params := t.call_param_types(method_name)
|
|
if params.len == 0 {
|
|
return t.transform_expr(base_id)
|
|
}
|
|
mut param_type := params[0].name()
|
|
if param_type.starts_with('&') {
|
|
param_type = param_type[1..]
|
|
}
|
|
target_type := t.trim_pointer_type(t.smartcast_target_type(sc))
|
|
if target_type.len > 0
|
|
&& t.normalize_type_alias(param_type) == t.normalize_type_alias(target_type) {
|
|
return t.apply_smartcast_contexts(t.make_plain_expr_for_smartcast(base_id),
|
|
t.original_expr_type(base_id), t.smartcasts_for(key))
|
|
}
|
|
method_receiver := method_name.all_before_last('.')
|
|
if method_receiver.len > 0 && t.is_sum_type_name(method_receiver)
|
|
&& t.normalize_type_alias(param_type) == t.normalize_type_alias(method_receiver) {
|
|
return t.make_plain_expr_for_smartcast(base_id)
|
|
}
|
|
mut sum_type := t.resolve_sum_name(sc.sum_type_name)
|
|
if sum_type.starts_with('&') {
|
|
sum_type = sum_type[1..]
|
|
}
|
|
if sum_type.len > 0 && t.normalize_type_alias(param_type) == t.normalize_type_alias(sum_type) {
|
|
return t.make_plain_expr_for_smartcast(base_id)
|
|
}
|
|
original_type := t.trim_pointer_type(t.original_expr_type(base_id))
|
|
if original_type.len > 0
|
|
&& t.normalize_type_alias(param_type) == t.normalize_type_alias(original_type) {
|
|
return t.make_plain_expr_for_smartcast(base_id)
|
|
}
|
|
return t.transform_expr(base_id)
|
|
}
|
|
|
|
fn (t &Transformer) receiver_method_return_type(method_name string, fallback string) string {
|
|
if !isnil(t.tc) {
|
|
if typ := t.tc.fn_ret_types[method_name] {
|
|
return t.normalize_type_alias(typ.name())
|
|
}
|
|
}
|
|
if ret := t.fn_ret_types[method_name] {
|
|
return ret
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
fn (t &Transformer) resolve_smartcast_sum_receiver_method(base_id flat.NodeId, method string) ?string {
|
|
key := t.expr_key(base_id)
|
|
sc := t.find_smartcast(key) or { return none }
|
|
variant := t.resolve_variant(sc.sum_type_name, sc.variant_name)
|
|
mut receiver_types := []string{}
|
|
receiver_types << variant
|
|
original_type := t.trim_pointer_type(t.original_expr_type(base_id))
|
|
if original_type.len > 0 && original_type !in receiver_types {
|
|
receiver_types << original_type
|
|
}
|
|
if sc.sum_type_name.len > 0 && sc.sum_type_name !in receiver_types {
|
|
receiver_types << sc.sum_type_name
|
|
}
|
|
sum_type := t.resolve_sum_name(sc.sum_type_name)
|
|
if sum_type.len > 0 && sum_type !in receiver_types {
|
|
receiver_types << sum_type
|
|
}
|
|
for parent in t.sum_type_parents_for_variant(sc.variant_name) {
|
|
if parent !in receiver_types {
|
|
receiver_types << parent
|
|
}
|
|
}
|
|
for receiver_type in receiver_types {
|
|
for candidate in t.receiver_method_candidates(receiver_type, method) {
|
|
if t.is_known_fn_name(candidate) {
|
|
return candidate
|
|
}
|
|
}
|
|
}
|
|
return none
|
|
}
|
|
|
|
fn (t &Transformer) sum_type_parents_for_variant(variant string) []string {
|
|
if parents := t.sum_variant_parents[variant] {
|
|
return parents.clone()
|
|
}
|
|
short := if variant.contains('.') { variant.all_after_last('.') } else { variant }
|
|
if short != variant {
|
|
if parents := t.sum_variant_parents[short] {
|
|
return parents.clone()
|
|
}
|
|
}
|
|
mut result := []string{}
|
|
for sum_name, variants in t.sum_types {
|
|
for v in variants {
|
|
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
|
|
if v == variant || short_v == short {
|
|
result << sum_name
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !isnil(t.tc) {
|
|
for sum_name, variants in t.tc.sum_types {
|
|
if sum_name in result {
|
|
continue
|
|
}
|
|
for v in variants {
|
|
short_v := if v.contains('.') { v.all_after_last('.') } else { v }
|
|
if v == variant || short_v == short {
|
|
result << sum_name
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
fn (t &Transformer) receiver_method_candidates(receiver_type string, method string) []string {
|
|
mut clean_type := receiver_type
|
|
if clean_type.starts_with('&') {
|
|
clean_type = clean_type[1..]
|
|
}
|
|
if clean_type.starts_with('map[') {
|
|
return t.map_receiver_method_candidates(clean_type, method)
|
|
}
|
|
mut candidates := []string{}
|
|
candidates << '${clean_type}.${method}'
|
|
if clean_type.starts_with('[]') {
|
|
elem_type := clean_type[2..]
|
|
short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
|
|
candidates << '[]${short_elem}.${method}'
|
|
if elem_type.contains('.') {
|
|
candidates << '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
|
|
}
|
|
} else if clean_type.contains('.') {
|
|
short_type := clean_type.all_after_last('.')
|
|
candidates << '${short_type}.${method}'
|
|
} else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
candidates << '${t.cur_module}.${clean_type}.${method}'
|
|
}
|
|
return candidates
|
|
}
|
|
|
|
fn (t &Transformer) map_receiver_method_candidates(receiver_type string, method string) []string {
|
|
clean_type := t.clean_map_type(receiver_type)
|
|
key_type := t.map_key_type(clean_type)
|
|
value_type := t.map_value_type(clean_type)
|
|
mut candidates := []string{}
|
|
candidates << '${clean_type}.${method}'
|
|
if key_type.len == 0 || value_type.len == 0 {
|
|
return candidates
|
|
}
|
|
short_value := if value_type.contains('.') {
|
|
value_type.all_after_last('.')
|
|
} else {
|
|
value_type
|
|
}
|
|
short_map := 'map[${key_type}]${short_value}'
|
|
if short_map != clean_type {
|
|
candidates << '${short_map}.${method}'
|
|
}
|
|
if value_type.contains('.') {
|
|
mod_name := value_type.all_before_last('.')
|
|
candidates << '${mod_name}.${short_map}.${method}'
|
|
} else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
candidates << '${t.cur_module}.${short_map}.${method}'
|
|
}
|
|
return candidates
|
|
}
|
|
|
|
fn (mut t Transformer) transform_receiver_method_args(node flat.Node, base_id flat.NodeId, method_name string) []flat.NodeId {
|
|
return t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
|
|
method_name), method_name)
|
|
}
|
|
|
|
fn (mut t Transformer) transform_receiver_method_args_with_base(node flat.Node, base flat.NodeId, method_name string) []flat.NodeId {
|
|
mut args := []flat.NodeId{cap: int(node.children_count)}
|
|
args << base
|
|
params := t.call_param_types(method_name)
|
|
param_offset := t.receiver_method_param_offset(base, node, params)
|
|
explicit_args := int(node.children_count) - 1
|
|
expected_explicit := params.len - param_offset
|
|
is_variadic := t.call_is_variadic(method_name) || (params.len > 0
|
|
&& params[params.len - 1] is types.Array && explicit_args > expected_explicit)
|
|
variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
|
|
params.len - 1
|
|
} else {
|
|
-1
|
|
}
|
|
mut i := 1
|
|
for i < node.children_count {
|
|
param_idx := (args.len - 1) + param_offset
|
|
arg_id := t.a.child(&node, i)
|
|
arg_node := t.a.nodes[int(arg_id)]
|
|
param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
|
|
if arg_node.kind == .field_init {
|
|
if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
|
|
args << packed_arg
|
|
i = t.next_non_field_init_arg(node, i)
|
|
continue
|
|
}
|
|
}
|
|
if variadic_idx >= 0 && param_idx == variadic_idx {
|
|
variadic_type := params[variadic_idx]
|
|
if variadic_type is types.Array {
|
|
remaining := int(node.children_count) - i
|
|
if remaining == 1 {
|
|
arg_type := t.node_type(arg_id)
|
|
if arg_type.starts_with('[]') {
|
|
args << t.transform_call_arg_for_param(arg_id, param_type)
|
|
} else {
|
|
args << t.pack_variadic_args(node, i, variadic_type.elem_type)
|
|
}
|
|
} else {
|
|
args << t.pack_variadic_args(node, i, variadic_type.elem_type)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
args << t.transform_call_arg_for_param(arg_id, param_type)
|
|
i++
|
|
}
|
|
if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
|
|
variadic_type := params[variadic_idx]
|
|
if variadic_type is types.Array {
|
|
args << t.pack_variadic_args(node, int(node.children_count), variadic_type.elem_type)
|
|
}
|
|
}
|
|
return args
|
|
}
|
|
|
|
fn (t &Transformer) receiver_method_param_offset(base_id flat.NodeId, node flat.Node, params []types.Type) int {
|
|
if params.len == 0 {
|
|
return 0
|
|
}
|
|
base_type := t.node_type(base_id)
|
|
first_type := t.normalize_type_alias(params[0].name())
|
|
clean_first := if first_type.starts_with('&') { first_type[1..] } else { first_type }
|
|
clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
|
if clean_first == clean_base
|
|
|| t.normalize_type_alias(clean_first) == t.normalize_type_alias(clean_base)
|
|
|| clean_first.all_after_last('.') == clean_base.all_after_last('.') {
|
|
return 1
|
|
}
|
|
if params.len >= int(node.children_count) {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
fn (mut t Transformer) try_lower_string_method_call(node flat.Node) ?flat.NodeId {
|
|
if node.children_count == 0 {
|
|
return none
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
if fn_node.kind != .selector || fn_node.children_count == 0 {
|
|
return none
|
|
}
|
|
method := fn_node.value
|
|
if method == 'count' && node.children_count == 2 && t.expr_uses_ident(t.a.child(&node, 1), 'it') {
|
|
return t.lower_string_count_call(node, fn_node)
|
|
}
|
|
if method !in ['replace', 'replace_once', 'trim', 'trim_left', 'trim_right', 'all_before',
|
|
'all_after', 'all_before_last', 'all_after_last', 'contains', 'starts_with', 'ends_with',
|
|
'bytes', 'substr', 'substr_unsafe', 'repeat', 'plus_two'] {
|
|
return none
|
|
}
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base_type := t.node_type(base_id)
|
|
if base_type != 'string' {
|
|
return none
|
|
}
|
|
mut args := []flat.NodeId{cap: int(node.children_count)}
|
|
args << t.transform_expr(base_id)
|
|
for i in 1 .. node.children_count {
|
|
args << t.transform_expr(t.a.child(&node, i))
|
|
}
|
|
ret_type := match method {
|
|
'contains', 'starts_with', 'ends_with' { 'bool' }
|
|
'bytes' { '[]u8' }
|
|
else { 'string' }
|
|
}
|
|
|
|
return t.make_call_typed('string__${method}', args, ret_type)
|
|
}
|
|
|
|
fn (mut t Transformer) lower_string_count_call(node flat.Node, fn_node flat.Node) ?flat.NodeId {
|
|
base_id := t.a.child(&fn_node, 0)
|
|
base_type := t.node_type(base_id)
|
|
if base_type != 'string' {
|
|
return none
|
|
}
|
|
base := t.stable_expr_for_reuse(base_id)
|
|
mut prefix := []flat.NodeId{}
|
|
t.drain_pending(mut prefix)
|
|
result_name := t.new_temp('count')
|
|
idx_name := t.new_temp('count_idx')
|
|
prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(0), 'int')
|
|
init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
|
|
cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
|
|
post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
|
|
elem_expr := t.make_index(base, t.make_ident(idx_name), 'u8')
|
|
elem_decl := t.make_decl_assign_typed('it', elem_expr, 'u8')
|
|
predicate_id := t.a.child(&node, 1)
|
|
old_it := t.var_type('it')
|
|
t.set_var_type('it', 'u8')
|
|
predicate := t.transform_expr(predicate_id)
|
|
if old_it.len > 0 {
|
|
t.set_var_type('it', old_it)
|
|
} else {
|
|
t.unset_var_type('it')
|
|
}
|
|
mut loop_body := []flat.NodeId{}
|
|
loop_body << elem_decl
|
|
t.drain_pending(mut loop_body)
|
|
inc := t.make_assign_op(t.make_ident(result_name), t.make_int_literal(1), .plus_assign)
|
|
loop_body << t.make_if(predicate, t.make_block(arr1(inc)), t.make_empty())
|
|
prefix << t.make_for_stmt(init, cond, post, loop_body, node)
|
|
for stmt in prefix {
|
|
t.pending_stmts << stmt
|
|
}
|
|
return t.make_ident(result_name)
|
|
}
|
|
|
|
fn (t &Transformer) expr_uses_ident(id flat.NodeId, name string) bool {
|
|
if int(id) < 0 {
|
|
return false
|
|
}
|
|
node := t.a.nodes[int(id)]
|
|
if node.kind == .ident && node.value == name {
|
|
return true
|
|
}
|
|
for i in 0 .. node.children_count {
|
|
if t.expr_uses_ident(t.a.child(&node, i), name) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// is_method_call checks if a .call node is a method call (child[0] is .selector).
|
|
// Returns true for `obj.method(args)` patterns.
|
|
fn (mut t Transformer) is_method_call(node flat.Node) bool {
|
|
if node.children_count == 0 {
|
|
return false
|
|
}
|
|
fn_id := t.a.children[node.children_start]
|
|
if int(fn_id) < 0 {
|
|
return false
|
|
}
|
|
fn_node := t.a.nodes[int(fn_id)]
|
|
return fn_node.kind == .selector
|
|
}
|
|
|
|
// get_call_return_type looks up the return type for a resolved call.
|
|
// Handles both checker-resolved calls and transform-time name resolution.
|
|
fn (t &Transformer) get_call_return_type(id flat.NodeId, node flat.Node) string {
|
|
if node.children_count > 0 {
|
|
fn_node := t.a.child_node(&node, 0)
|
|
if fn_node.kind == .selector && fn_node.value in ['clone', 'reverse']
|
|
&& fn_node.children_count > 0 {
|
|
base_id := t.a.child(fn_node, 0)
|
|
base_type := t.node_type(base_id)
|
|
clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
|
|
if clean_type.starts_with('[]') {
|
|
return clean_type
|
|
}
|
|
}
|
|
if fn_node.kind == .selector && fn_node.value == 'keys' && fn_node.children_count > 0 {
|
|
base_id := t.a.child(fn_node, 0)
|
|
base_type := t.node_type(base_id)
|
|
clean_type := t.clean_map_type(base_type)
|
|
if clean_type.starts_with('map[') {
|
|
key_type := t.map_key_type(clean_type)
|
|
if key_type.len > 0 {
|
|
return '[]${key_type}'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !isnil(t.tc) {
|
|
if name := t.tc.resolved_call_name(id) {
|
|
if ret := t.tc.fn_ret_types[name] {
|
|
return t.call_return_type_name(ret.name(), node)
|
|
}
|
|
}
|
|
}
|
|
name := t.resolve_call_name(node)
|
|
if name.len == 0 {
|
|
return ''
|
|
}
|
|
if !isnil(t.tc) {
|
|
if ret := t.tc.fn_ret_types[name] {
|
|
return t.call_return_type_name(ret.name(), node)
|
|
}
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
qname := '${t.cur_module}.${name}'
|
|
if ret := t.tc.fn_ret_types[qname] {
|
|
return t.call_return_type_name(ret.name(), node)
|
|
}
|
|
}
|
|
}
|
|
// Try exact name first
|
|
if ret := t.fn_ret_types[name] {
|
|
return t.call_return_type_name(ret, node)
|
|
}
|
|
// Try qualified with current module
|
|
if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
|
|
qname := '${t.cur_module}.${name}'
|
|
if ret := t.fn_ret_types[qname] {
|
|
return t.call_return_type_name(ret, node)
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
fn (t &Transformer) call_return_type_name(ret_name string, node flat.Node) string {
|
|
mut typ := ret_name
|
|
if node.value.len > 0 {
|
|
generic_arg := t.normalize_type_in_module(node.value, t.cur_module)
|
|
if generic_arg.len > 0 {
|
|
typ = t.specialize_generic_type_name(typ, generic_arg)
|
|
}
|
|
}
|
|
if t.is_optional_type_name(typ) {
|
|
return typ
|
|
}
|
|
return t.normalize_type_alias(typ)
|
|
}
|
|
|
|
fn (t &Transformer) specialize_generic_type_name(typ string, generic_arg string) string {
|
|
clean := typ.trim_space()
|
|
if clean.len == 0 || generic_arg.len == 0 {
|
|
return typ
|
|
}
|
|
if clean == 'T' {
|
|
return generic_arg
|
|
}
|
|
if clean.starts_with('&') {
|
|
return '&' + t.specialize_generic_type_name(clean[1..], generic_arg)
|
|
}
|
|
if clean.starts_with('[]') {
|
|
return '[]' + t.specialize_generic_type_name(clean[2..], generic_arg)
|
|
}
|
|
if clean.starts_with('?') {
|
|
return '?' + t.specialize_generic_type_name(clean[1..], generic_arg)
|
|
}
|
|
if clean.starts_with('!') {
|
|
return '!' + t.specialize_generic_type_name(clean[1..], generic_arg)
|
|
}
|
|
if clean.starts_with('...') {
|
|
return '...' + t.specialize_generic_type_name(clean[3..], generic_arg)
|
|
}
|
|
if clean.starts_with('map[') {
|
|
bracket_end := clean.index(']') or { return typ }
|
|
key_type := t.specialize_generic_type_name(clean[4..bracket_end], generic_arg)
|
|
value_type := t.specialize_generic_type_name(clean[bracket_end + 1..], generic_arg)
|
|
return 'map[${key_type}]${value_type}'
|
|
}
|
|
if clean.starts_with('[') {
|
|
bracket_end := clean.index(']') or { return typ }
|
|
return clean[..bracket_end + 1] + t.specialize_generic_type_name(clean[bracket_end +
|
|
1..], generic_arg)
|
|
}
|
|
return typ
|
|
}
|