diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8c65ad2688..020ddfd7f7 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2081,8 +2081,13 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { c.error('optional type cannot be called directly', call_expr.left.position()) return ast.void_type } - if left_type_sym.kind in [.sum_type, .interface_] && method_name == 'type_name' { - return ast.string_type + if left_type_sym.kind in [.sum_type, .interface_] { + if method_name == 'type_name' { + return ast.string_type + } + if method_name == 'type_idx' { + return ast.int_type + } } if left_type == ast.void_type { c.error('`void` type has no methods', call_expr.left.position()) @@ -3374,10 +3379,12 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { return ast.int_type } else { - if node.field_name != 'name' { - c.error('invalid field `.$node.field_name` for type `$node.expr`', - node.pos) + if node.field_name == 'name' { + return ast.string_type + } else if node.field_name == 'idx' { + return ast.int_type } + c.error('invalid field `.$node.field_name` for type `$node.expr`', node.pos) return ast.string_type } } @@ -8007,6 +8014,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { c.error('method overrides built-in array method', node.pos) } else if sym.kind == .sum_type && node.name == 'type_name' { c.error('method overrides built-in sum type method', node.pos) + } else if sym.kind == .sum_type && node.name == 'type_idx' { + c.error('method overrides built-in sum type method', node.pos) } else if sym.kind == .multi_return { c.error('cannot define method on multi-value', node.method_type_pos) } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index d483e75d0a..d5ade09750 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -528,7 +528,7 @@ pub fn (mut g Gen) finish() { pub fn (mut g Gen) write_typeof_functions() { g.writeln('') g.writeln('// >> typeof() support for sum types / interfaces') - for typ in g.table.type_symbols { + for ityp, typ in g.table.type_symbols { if typ.kind == .sum_type { sum_info := typ.info as ast.SumType if sum_info.is_generic { @@ -554,6 +554,26 @@ pub fn (mut g Gen) write_typeof_functions() { g.writeln('\t}') } g.writeln('}') + g.writeln('') + g.writeln('int v_typeof_sumtype_idx_${typ.cname}(int sidx) { /* $typ.name */ ') + if g.pref.build_mode == .build_module { + g.writeln('\t\tif( sidx == _v_type_idx_${typ.cname}() ) return ${int(ityp)};') + for v in sum_info.variants { + subtype := g.table.get_type_symbol(v) + g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return ${int(v)};') + } + g.writeln('\treturn ${int(ityp)};') + } else { + tidx := g.table.find_type_idx(typ.name) + g.writeln('\tswitch(sidx) {') + g.writeln('\t\tcase $tidx: return ${int(ityp)};') + for v in sum_info.variants { + g.writeln('\t\tcase $v: return ${int(v)};') + } + g.writeln('\t\tdefault: return ${int(ityp)};') + g.writeln('\t}') + } + g.writeln('}') } else if typ.kind == .interface_ { inter_info := typ.info as ast.Interface if inter_info.is_generic { @@ -566,6 +586,14 @@ pub fn (mut g Gen) write_typeof_functions() { } g.writeln('\treturn "unknown ${util.strip_main_name(typ.name)}";') g.writeln('}') + g.writeln('') + g.writeln('int v_typeof_interface_idx_${typ.cname}(int sidx) { /* $typ.name */ ') + for t in inter_info.types { + subtype := g.table.get_type_symbol(t) + g.writeln('\tif (sidx == _${typ.cname}_${subtype.cname}_index) return ${int(t)};') + } + g.writeln('\treturn ${int(ityp)};') + g.writeln('}') } } g.writeln('// << typeof() support for sum types') @@ -3663,6 +3691,10 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { // typeof(expr).name g.type_name(node.name_type) return + } else if node.field_name == 'idx' { + // typeof(expr).idx + g.write(int(g.unwrap_generic(node.name_type)).str()) + return } g.error('unknown generic field', node.pos) } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 52df03a41e..7838221489 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -628,6 +628,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } } +fn (mut g Gen) conversion_function_call(prefix string, postfix string, node ast.CallExpr) { + g.write('${prefix}( (') + g.expr(node.left) + dot := if node.left_type.is_ptr() { '->' } else { '.' } + g.write(')${dot}_typ )$postfix') +} + fn (mut g Gen) method_call(node ast.CallExpr) { // TODO: there are still due to unchecked exprs (opt/some fn arg) if node.left_type == 0 { @@ -744,20 +751,33 @@ fn (mut g Gen) method_call(node ast.CallExpr) { return } - if left_sym.kind == .sum_type && node.name == 'type_name' { - g.write('charptr_vstring_literal( /* $left_sym.name */ v_typeof_sumtype_${typ_sym.cname}( (') - g.expr(node.left) - dot := if node.left_type.is_ptr() { '->' } else { '.' } - g.write(')${dot}_typ ))') - return - } - if left_sym.kind == .interface_ && node.name == 'type_name' { - g.write('charptr_vstring_literal( /* $left_sym.name */ v_typeof_interface_${typ_sym.cname}( (') - g.expr(node.left) - dot := if node.left_type.is_ptr() { '->' } else { '.' } - g.write(')${dot}_typ ))') - return + if left_sym.kind in [.sum_type, .interface_] { + if node.name == 'type_name' { + if left_sym.kind == .sum_type { + g.conversion_function_call('charptr_vstring_literal( /* $left_sym.name */ v_typeof_sumtype_$typ_sym.cname', + ')', node) + return + } + if left_sym.kind == .interface_ { + g.conversion_function_call('charptr_vstring_literal( /* $left_sym.name */ v_typeof_interface_$typ_sym.cname', + ')', node) + return + } + } + if node.name == 'type_idx' { + if left_sym.kind == .sum_type { + g.conversion_function_call('/* $left_sym.name */ v_typeof_sumtype_idx_$typ_sym.cname', + '', node) + return + } + if left_sym.kind == .interface_ { + g.conversion_function_call('/* $left_sym.name */ v_typeof_interface_idx_$typ_sym.cname', + '', node) + return + } + } } + if node.name == 'str' { mut rec_type := node.receiver_type if rec_type.has_flag(.shared_f) { diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index f282b5168b..7cd4e606e6 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -514,8 +514,8 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { line_nr := p.tok.line_nr name := p.check_name() - if name == 'type_name' { - p.error_with_pos('cannot override built-in method `type_name`', method_start_pos) + if name in ['type_name', 'type_idx'] { + p.error_with_pos('cannot override built-in method `$name`', method_start_pos) return ast.InterfaceDecl{} } if ts.has_method(name) { diff --git a/vlib/v/tests/type_idx_test.v b/vlib/v/tests/type_idx_test.v new file mode 100644 index 0000000000..aa8fb0bb45 --- /dev/null +++ b/vlib/v/tests/type_idx_test.v @@ -0,0 +1,101 @@ +// NB: .type_name() and .type_idx() called on an interface instance are more expensive +// than typeof(instance).name and typeof(instance).idx, since they will search and return +// the name and type index of the concrete interface instance. +// +// typeof(interface_instance).name returns the interface name, in the example here it will +// be always 'Animal'. +// +// interface_instance.type_name() will return 'Dog' or 'Cat' in this example, depending on +// what instance it is called. +// +// Similarly, typeof(interface_instance).idx will always return the same type index for all +// kinds of Animal. + +interface Animal { + name string +} + +struct Dog { + name string +} + +struct Cat { + name string +} + +type SumType = int | string + +fn test_type_idx() { + d := Dog{ + name: 'Carlos' + } + c := Cat{ + name: 'Tom' + } + ad := Animal(d) + ac := Animal(c) + dump(ad) + dump(ac) + divider___() + dump(typeof(ad).name) + dump(typeof(ac).name) + assert typeof(ad).name == 'Animal' + assert typeof(ac).name == 'Animal' + dump(typeof(ad).idx) + dump(typeof(ac).idx) + assert typeof(ad).idx == typeof(ac).idx + divider___() + dump(ad.type_name()) + dump(ac.type_name()) + assert ad.type_name() == 'Dog' + assert ac.type_name() == 'Cat' + dump(ad.type_idx()) + dump(ac.type_idx()) + assert ad.type_idx() != ac.type_idx() + assert ac.type_idx() != typeof(ad).idx + divider___() + dump(typeof(d).name) + dump(typeof(c).name) + assert typeof(d).name == 'Dog' + assert typeof(c).name == 'Cat' + dump(typeof(d).idx) + dump(typeof(c).idx) + assert typeof(d).idx != typeof(c).idx + assert typeof(d).idx != typeof(ad).idx +} + +fn test_sumtype_type_idx() { + s := 'abc' + i := 123 + ss := SumType(s) + si := SumType(i) + divider___() + dump(s) + dump(i) + dump(ss) + dump(si) + dump(typeof(s).idx) + dump(typeof(i).idx) + dump(typeof(ss).idx) + dump(typeof(si).idx) + assert typeof(ss).idx != typeof(s).idx + assert typeof(s).idx != typeof(i).idx + assert typeof(ss).idx == typeof(si).idx + divider___() + dump(ss.type_name()) + dump(si.type_name()) + assert ss.type_name() == 'string' + assert si.type_name() == 'int' + assert ss.type_name() != si.type_name() + dump(ss.type_idx()) + dump(si.type_idx()) + assert ss.type_idx() == typeof(s).idx + assert si.type_idx() == typeof(i).idx + assert ss.type_idx() != si.type_idx() + assert typeof(ss).idx != ss.type_idx() + assert typeof(si).idx != si.type_idx() +} + +fn divider___() { + println('------------------------------------------------------------') +}