all: support runtime interface conversions (#11212)
parent
7c9a1defa4
commit
7d9969ac17
|
@ -797,6 +797,8 @@ pub mut:
|
||||||
fields []StructField
|
fields []StructField
|
||||||
methods []Fn
|
methods []Fn
|
||||||
ifaces []Type
|
ifaces []Type
|
||||||
|
// `I1 is I2` conversions
|
||||||
|
conversions map[int][]Type
|
||||||
// generic interface support
|
// generic interface support
|
||||||
is_generic bool
|
is_generic bool
|
||||||
generic_types []Type
|
generic_types []Type
|
||||||
|
|
|
@ -5260,6 +5260,8 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
|
||||||
if !c.table.sumtype_has_variant(node.expr_type, node.typ) {
|
if !c.table.sumtype_has_variant(node.expr_type, node.typ) {
|
||||||
c.error('cannot cast `$expr_type_sym.name` to `$type_sym.name`', node.pos)
|
c.error('cannot cast `$expr_type_sym.name` to `$type_sym.name`', node.pos)
|
||||||
}
|
}
|
||||||
|
} else if expr_type_sym.kind == .interface_ && type_sym.kind == .interface_ {
|
||||||
|
c.ensure_type_exists(node.typ, node.pos) or {}
|
||||||
} else if node.expr_type != node.typ {
|
} else if node.expr_type != node.typ {
|
||||||
mut s := 'cannot cast non-sum type `$expr_type_sym.name` using `as`'
|
mut s := 'cannot cast non-sum type `$expr_type_sym.name` using `as`'
|
||||||
if type_sym.kind == .sum_type {
|
if type_sym.kind == .sum_type {
|
||||||
|
@ -6569,9 +6571,14 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
|
||||||
right_type = c.unwrap_generic(right_type)
|
right_type = c.unwrap_generic(right_type)
|
||||||
if right_type != ast.Type(0) {
|
if right_type != ast.Type(0) {
|
||||||
left_sym := c.table.get_type_symbol(node.left_type)
|
left_sym := c.table.get_type_symbol(node.left_type)
|
||||||
|
right_sym := c.table.get_type_symbol(right_type)
|
||||||
expr_type := c.unwrap_generic(c.expr(node.left))
|
expr_type := c.unwrap_generic(c.expr(node.left))
|
||||||
if left_sym.kind == .interface_ {
|
if left_sym.kind == .interface_ {
|
||||||
c.type_implements(right_type, expr_type, node.pos)
|
if right_sym.kind != .interface_ {
|
||||||
|
c.type_implements(right_type, expr_type, node.pos)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
} else if !c.check_types(right_type, expr_type) {
|
} else if !c.check_types(right_type, expr_type) {
|
||||||
expect_str := c.table.type_to_str(right_type)
|
expect_str := c.table.type_to_str(right_type)
|
||||||
expr_str := c.table.type_to_str(expr_type)
|
expr_str := c.table.type_to_str(expr_type)
|
||||||
|
|
|
@ -580,6 +580,7 @@ pub fn (mut g Gen) write_typeof_functions() {
|
||||||
if inter_info.is_generic {
|
if inter_info.is_generic {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
g.definitions.writeln('static char * v_typeof_interface_${typ.cname}(int sidx);')
|
||||||
g.writeln('static char * v_typeof_interface_${typ.cname}(int sidx) { /* $typ.name */ ')
|
g.writeln('static char * v_typeof_interface_${typ.cname}(int sidx) { /* $typ.name */ ')
|
||||||
for t in inter_info.types {
|
for t in inter_info.types {
|
||||||
subtype := g.table.get_type_symbol(t)
|
subtype := g.table.get_type_symbol(t)
|
||||||
|
@ -6436,8 +6437,8 @@ fn (mut g Gen) as_cast(node ast.AsCast) {
|
||||||
// g.insert_before('
|
// g.insert_before('
|
||||||
styp := g.typ(node.typ)
|
styp := g.typ(node.typ)
|
||||||
sym := g.table.get_type_symbol(node.typ)
|
sym := g.table.get_type_symbol(node.typ)
|
||||||
expr_type_sym := g.table.get_type_symbol(node.expr_type)
|
mut expr_type_sym := g.table.get_type_symbol(node.expr_type)
|
||||||
if expr_type_sym.info is ast.SumType {
|
if mut expr_type_sym.info is ast.SumType {
|
||||||
dot := if node.expr_type.is_ptr() { '->' } else { '.' }
|
dot := if node.expr_type.is_ptr() { '->' } else { '.' }
|
||||||
g.write('/* as */ *($styp*)__as_cast(')
|
g.write('/* as */ *($styp*)__as_cast(')
|
||||||
g.write('(')
|
g.write('(')
|
||||||
|
@ -6463,6 +6464,18 @@ fn (mut g Gen) as_cast(node ast.AsCast) {
|
||||||
variant_sym := g.table.get_type_symbol(variant)
|
variant_sym := g.table.get_type_symbol(variant)
|
||||||
g.as_cast_type_names[idx] = variant_sym.name
|
g.as_cast_type_names[idx] = variant_sym.name
|
||||||
}
|
}
|
||||||
|
} else if expr_type_sym.kind == .interface_ && sym.kind == .interface_ {
|
||||||
|
g.write('I_${expr_type_sym.cname}_as_I_${sym.cname}(')
|
||||||
|
g.expr(node.expr)
|
||||||
|
g.write(')')
|
||||||
|
|
||||||
|
mut info := expr_type_sym.info as ast.Interface
|
||||||
|
if node.typ !in info.conversions {
|
||||||
|
left_variants := g.table.iface_types[expr_type_sym.name]
|
||||||
|
right_variants := g.table.iface_types[sym.name]
|
||||||
|
info.conversions[node.typ] = left_variants.filter(it in right_variants)
|
||||||
|
}
|
||||||
|
expr_type_sym.info = info
|
||||||
} else {
|
} else {
|
||||||
g.expr(node.expr)
|
g.expr(node.expr)
|
||||||
}
|
}
|
||||||
|
@ -6486,6 +6499,7 @@ fn (g Gen) as_cast_name_table() string {
|
||||||
// Generates interface table and interface indexes
|
// Generates interface table and interface indexes
|
||||||
fn (mut g Gen) interface_table() string {
|
fn (mut g Gen) interface_table() string {
|
||||||
mut sb := strings.new_builder(100)
|
mut sb := strings.new_builder(100)
|
||||||
|
mut conversion_functions := strings.new_builder(100)
|
||||||
for ityp in g.table.type_symbols {
|
for ityp in g.table.type_symbols {
|
||||||
if ityp.kind != .interface_ {
|
if ityp.kind != .interface_ {
|
||||||
continue
|
continue
|
||||||
|
@ -6669,11 +6683,42 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
|
||||||
}
|
}
|
||||||
iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base
|
iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base
|
||||||
if g.pref.build_mode != .build_module {
|
if g.pref.build_mode != .build_module {
|
||||||
sb.writeln('int $interface_index_name = $iin_idx;')
|
sb.writeln('const int $interface_index_name = $iin_idx;')
|
||||||
} else {
|
} else {
|
||||||
sb.writeln('int $interface_index_name;')
|
sb.writeln('extern const int $interface_index_name;')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for vtyp, variants in inter_info.conversions {
|
||||||
|
vsym := g.table.get_type_symbol(vtyp)
|
||||||
|
conversion_functions.write_string('static inline bool I_${interface_name}_is_I_${vsym.cname}($interface_name x) {\n\treturn ')
|
||||||
|
for i, variant in variants {
|
||||||
|
variant_sym := g.table.get_type_symbol(variant)
|
||||||
|
if i > 0 {
|
||||||
|
conversion_functions.write_string(' || ')
|
||||||
|
}
|
||||||
|
conversion_functions.write_string('(x._typ == _${interface_name}_${variant_sym.cname}_index)')
|
||||||
|
}
|
||||||
|
conversion_functions.writeln(';\n}')
|
||||||
|
|
||||||
|
conversion_functions.writeln('static inline $vsym.cname I_${interface_name}_as_I_${vsym.cname}($interface_name x) {')
|
||||||
|
for variant in variants {
|
||||||
|
variant_sym := g.table.get_type_symbol(variant)
|
||||||
|
conversion_functions.writeln('\tif (x._typ == _${interface_name}_${variant_sym.cname}_index) return I_${variant_sym.cname}_to_Interface_${vsym.cname}(x._$variant_sym.cname);')
|
||||||
|
}
|
||||||
|
pmessage := 'string__plus(string__plus(tos3("`as_cast`: cannot convert "), tos3(v_typeof_interface_${interface_name}(x._typ))), tos3(" to ${util.strip_main_name(vsym.name)}"))'
|
||||||
|
if g.pref.is_debug {
|
||||||
|
// TODO: actually return a valid position here
|
||||||
|
conversion_functions.write_string('\tpanic_debug(1, tos3("builtin.v"), tos3("builtin"), tos3("__as_cast"), ')
|
||||||
|
conversion_functions.write_string(pmessage)
|
||||||
|
conversion_functions.writeln(');')
|
||||||
|
} else {
|
||||||
|
conversion_functions.write_string('\t_v_panic(')
|
||||||
|
conversion_functions.write_string(pmessage)
|
||||||
|
conversion_functions.writeln(');')
|
||||||
|
}
|
||||||
|
conversion_functions.writeln('\treturn ($vsym.cname){0};')
|
||||||
|
conversion_functions.writeln('}')
|
||||||
|
}
|
||||||
sb.writeln('// ^^^ number of types for interface $interface_name: ${current_iinidx - iinidx_minimum_base}')
|
sb.writeln('// ^^^ number of types for interface $interface_name: ${current_iinidx - iinidx_minimum_base}')
|
||||||
if iname_table_length == 0 {
|
if iname_table_length == 0 {
|
||||||
methods_struct.writeln('')
|
methods_struct.writeln('')
|
||||||
|
@ -6691,6 +6736,7 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
|
||||||
}
|
}
|
||||||
sb.writeln(cast_functions.str())
|
sb.writeln(cast_functions.str())
|
||||||
}
|
}
|
||||||
|
sb.writeln(conversion_functions.str())
|
||||||
return sb.str()
|
return sb.str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -396,6 +396,13 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
|
||||||
|
|
||||||
// infix_expr_is_op generates code for `is` and `!is`
|
// infix_expr_is_op generates code for `is` and `!is`
|
||||||
fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
|
fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
|
||||||
|
sym := g.table.get_type_symbol(node.left_type)
|
||||||
|
right_sym := g.table.get_type_symbol(node.right_type)
|
||||||
|
if sym.kind == .interface_ && right_sym.kind == .interface_ {
|
||||||
|
g.gen_interface_is_op(node)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cmp_op := if node.op == .key_is { '==' } else { '!=' }
|
cmp_op := if node.op == .key_is { '==' } else { '!=' }
|
||||||
g.write('(')
|
g.write('(')
|
||||||
g.expr(node.left)
|
g.expr(node.left)
|
||||||
|
@ -405,7 +412,6 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
|
||||||
} else {
|
} else {
|
||||||
g.write('.')
|
g.write('.')
|
||||||
}
|
}
|
||||||
sym := g.table.get_type_symbol(node.left_type)
|
|
||||||
if sym.kind == .interface_ {
|
if sym.kind == .interface_ {
|
||||||
g.write('_typ $cmp_op ')
|
g.write('_typ $cmp_op ')
|
||||||
// `_Animal_Dog_index`
|
// `_Animal_Dog_index`
|
||||||
|
@ -423,6 +429,29 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
|
||||||
g.expr(node.right)
|
g.expr(node.right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) gen_interface_is_op(node ast.InfixExpr) {
|
||||||
|
mut left_sym := g.table.get_type_symbol(node.left_type)
|
||||||
|
right_sym := g.table.get_type_symbol(node.right_type)
|
||||||
|
|
||||||
|
mut info := left_sym.info as ast.Interface
|
||||||
|
|
||||||
|
common_variants := info.conversions[node.right_type] or {
|
||||||
|
left_variants := g.table.iface_types[left_sym.name]
|
||||||
|
right_variants := g.table.iface_types[right_sym.name]
|
||||||
|
c := left_variants.filter(it in right_variants)
|
||||||
|
info.conversions[node.right_type] = c
|
||||||
|
c
|
||||||
|
}
|
||||||
|
left_sym.info = info
|
||||||
|
if common_variants.len == 0 {
|
||||||
|
g.write('false')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.write('I_${left_sym.cname}_is_I_${right_sym.cname}(')
|
||||||
|
g.expr(node.left)
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
|
|
||||||
// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%`
|
// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%`
|
||||||
// It handles operator overloading when necessary
|
// It handles operator overloading when necessary
|
||||||
fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) {
|
fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) {
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
interface Widget {
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizableWidget {
|
||||||
|
Widget
|
||||||
|
resize(x int, y int) int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(w Widget) {
|
||||||
|
// w.resize(10, 20) // <- this won't work, since all Widgets may not implement resize()
|
||||||
|
|
||||||
|
// however, we can check if the underlying type of w implements a different interface:
|
||||||
|
if w is ResizableWidget {
|
||||||
|
assert w is WidgetB
|
||||||
|
rw := w as ResizableWidget
|
||||||
|
assert rw is WidgetB
|
||||||
|
// if so, we can now safely call that extra method
|
||||||
|
assert rw.resize(10, 20) == 200
|
||||||
|
} else {
|
||||||
|
assert w is WidgetA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements Widget, but not ResizableWidget
|
||||||
|
struct WidgetA {
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements both Widget and ResizableWidget
|
||||||
|
struct WidgetB {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w WidgetB) resize(x int, y int) int {
|
||||||
|
return x * y
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_interface_runtime_conversions() {
|
||||||
|
draw(WidgetA{})
|
||||||
|
draw(WidgetB{})
|
||||||
|
}
|
Loading…
Reference in New Issue