cgen: support `typeof(x).idx`, as well as `iface.type_idx()` (#11178)
parent
8dc043ba2c
commit
deb26b92b9
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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('------------------------------------------------------------')
|
||||
}
|
Loading…
Reference in New Issue