cgen: support `typeof(x).idx`, as well as `iface.type_idx()` (#11178)

pull/11184/head
Delyan Angelov 2021-08-14 17:22:25 +03:00 committed by GitHub
parent 8dc043ba2c
commit deb26b92b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 183 additions and 21 deletions

View File

@ -2081,9 +2081,14 @@ 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' {
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())
return ast.void_type
@ -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)
}

View File

@ -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)
}

View File

@ -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 ))')
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_ && 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 ))')
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) {

View File

@ -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) {

View File

@ -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('------------------------------------------------------------')
}