mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker, cgen: add sumtype-like smartcasting capabilites to interfaces (#9256)
This commit is contained in:
parent
78e3bb748b
commit
4feb09fa5b
@ -479,12 +479,12 @@ pub:
|
||||
is_arg bool // fn args should not be autofreed
|
||||
is_auto_deref bool
|
||||
pub mut:
|
||||
typ Type
|
||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
||||
sum_type_casts []Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||
typ Type
|
||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
||||
smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||
// TODO: move this to a real docs site later
|
||||
// 10 <- original type (orig_type)
|
||||
// [11, 12, 13] <- cast order (sum_type_casts)
|
||||
// [11, 12, 13] <- cast order (smartcasts)
|
||||
// 12 <- the current casted type (typ)
|
||||
pos token.Position
|
||||
is_used bool
|
||||
@ -499,15 +499,15 @@ pub mut:
|
||||
// struct fields change type in scopes
|
||||
pub struct ScopeStructField {
|
||||
pub:
|
||||
struct_type Type // type of struct
|
||||
name string
|
||||
pos token.Position
|
||||
typ Type
|
||||
sum_type_casts []Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
||||
struct_type Type // type of struct
|
||||
name string
|
||||
pos token.Position
|
||||
typ Type
|
||||
smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed
|
||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
||||
// TODO: move this to a real docs site later
|
||||
// 10 <- original type (orig_type)
|
||||
// [11, 12, 13] <- cast order (sum_type_casts)
|
||||
// [11, 12, 13] <- cast order (smartcasts)
|
||||
// 12 <- the current casted type (typ)
|
||||
}
|
||||
|
||||
@ -691,9 +691,8 @@ pub:
|
||||
body_pos token.Position
|
||||
comments []Comment
|
||||
pub mut:
|
||||
stmts []Stmt
|
||||
smartcast bool // true when cond is `x is SumType`, set in checker.if_expr // no longer needed with union sum types TODO: remove
|
||||
scope &Scope
|
||||
stmts []Stmt
|
||||
scope &Scope
|
||||
}
|
||||
|
||||
pub struct UnsafeExpr {
|
||||
|
@ -2350,9 +2350,7 @@ fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.P
|
||||
c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`",
|
||||
pos)
|
||||
}
|
||||
if utyp !in inter_sym.info.types && typ_sym.kind != .interface_ {
|
||||
inter_sym.info.types << utyp
|
||||
}
|
||||
inter_sym.info.types << utyp
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -2593,10 +2591,10 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ
|
||||
c.error('field `${sym.name}.$field_name` is not public', selector_expr.pos)
|
||||
}
|
||||
field_sym := c.table.get_type_symbol(field.typ)
|
||||
if field_sym.kind == .sum_type {
|
||||
if field_sym.kind in [.sum_type, .interface_] {
|
||||
if !prevent_sum_type_unwrapping_once {
|
||||
if scope_field := selector_expr.scope.find_struct_field(utyp, field_name) {
|
||||
return scope_field.sum_type_casts.last()
|
||||
return scope_field.smartcasts.last()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3729,9 +3727,8 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
|
||||
left_type := c.expr(infix.left)
|
||||
left_sym := c.table.get_type_symbol(left_type)
|
||||
if is_variable {
|
||||
if left_sym.kind == .sum_type {
|
||||
c.smartcast_sumtype(infix.left, infix.left_type, right_expr.typ, mut
|
||||
node.scope)
|
||||
if left_sym.kind in [.sum_type, .interface_] {
|
||||
c.smartcast(infix.left, infix.left_type, right_expr.typ, mut node.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4658,10 +4655,10 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) ast.Type {
|
||||
c.error('undefined variable `$ident.name` (used before declaration)',
|
||||
ident.pos)
|
||||
}
|
||||
is_sum_type_cast := obj.sum_type_casts.len != 0
|
||||
is_sum_type_cast := obj.smartcasts.len != 0
|
||||
&& !c.prevent_sum_type_unwrapping_once
|
||||
c.prevent_sum_type_unwrapping_once = false
|
||||
mut typ := if is_sum_type_cast { obj.sum_type_casts.last() } else { obj.typ }
|
||||
mut typ := if is_sum_type_cast { obj.smartcasts.last() } else { obj.typ }
|
||||
if typ == 0 {
|
||||
if mut obj.expr is ast.Ident {
|
||||
if obj.expr.kind == .unresolved {
|
||||
@ -4988,9 +4985,9 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
}
|
||||
branch_exprs[key] = val + 1
|
||||
}
|
||||
// when match is sum type matching, then register smart cast for every branch
|
||||
// when match is type matching, then register smart cast for every branch
|
||||
if expr_types.len > 0 {
|
||||
if cond_type_sym.kind == .sum_type {
|
||||
if cond_type_sym.kind in [.sum_type, .interface_] {
|
||||
mut expr_type := ast.Type(0)
|
||||
if expr_types.len > 1 {
|
||||
mut agg_name := strings.new_builder(20)
|
||||
@ -5025,7 +5022,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
} else {
|
||||
expr_type = expr_types[0].typ
|
||||
}
|
||||
c.smartcast_sumtype(node.cond, node.cond_type, expr_type, mut branch.scope)
|
||||
c.smartcast(node.cond, node.cond_type, expr_type, mut branch.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5106,11 +5103,13 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
|
||||
}
|
||||
|
||||
// smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope
|
||||
fn (c &Checker) smartcast_sumtype(expr ast.Expr, cur_type ast.Type, to_type ast.Type, mut scope ast.Scope) {
|
||||
match mut expr {
|
||||
fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) {
|
||||
sym := c.table.get_type_symbol(cur_type)
|
||||
to_type := if sym.kind == .interface_ { to_type_.to_ptr() } else { to_type_ }
|
||||
match expr {
|
||||
ast.SelectorExpr {
|
||||
mut is_mut := false
|
||||
mut sum_type_casts := []ast.Type{}
|
||||
mut smartcasts := []ast.Type{}
|
||||
expr_sym := c.table.get_type_symbol(expr.expr_type)
|
||||
mut orig_type := 0
|
||||
if field := c.table.find_field(expr_sym, expr.field_name) {
|
||||
@ -5125,16 +5124,16 @@ fn (c &Checker) smartcast_sumtype(expr ast.Expr, cur_type ast.Type, to_type ast.
|
||||
}
|
||||
}
|
||||
if field := scope.find_struct_field(expr.expr_type, expr.field_name) {
|
||||
sum_type_casts << field.sum_type_casts
|
||||
smartcasts << field.smartcasts
|
||||
}
|
||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||
if !is_mut || expr.is_mut {
|
||||
sum_type_casts << to_type
|
||||
smartcasts << to_type
|
||||
scope.register_struct_field(ast.ScopeStructField{
|
||||
struct_type: expr.expr_type
|
||||
name: expr.field_name
|
||||
typ: cur_type
|
||||
sum_type_casts: sum_type_casts
|
||||
smartcasts: smartcasts
|
||||
pos: expr.pos
|
||||
orig_type: orig_type
|
||||
})
|
||||
@ -5142,12 +5141,12 @@ fn (c &Checker) smartcast_sumtype(expr ast.Expr, cur_type ast.Type, to_type ast.
|
||||
}
|
||||
ast.Ident {
|
||||
mut is_mut := false
|
||||
mut sum_type_casts := []ast.Type{}
|
||||
mut smartcasts := []ast.Type{}
|
||||
mut is_already_casted := false
|
||||
mut orig_type := 0
|
||||
if mut expr.obj is ast.Var {
|
||||
is_mut = expr.obj.is_mut
|
||||
sum_type_casts << expr.obj.sum_type_casts
|
||||
smartcasts << expr.obj.smartcasts
|
||||
is_already_casted = expr.obj.pos.pos == expr.pos.pos
|
||||
if orig_type == 0 {
|
||||
orig_type = expr.obj.typ
|
||||
@ -5155,14 +5154,14 @@ fn (c &Checker) smartcast_sumtype(expr ast.Expr, cur_type ast.Type, to_type ast.
|
||||
}
|
||||
// smartcast either if the value is immutable or if the mut argument is explicitly given
|
||||
if (!is_mut || expr.is_mut) && !is_already_casted {
|
||||
sum_type_casts << to_type
|
||||
smartcasts << to_type
|
||||
scope.register(ast.Var{
|
||||
name: expr.name
|
||||
typ: cur_type
|
||||
pos: expr.pos
|
||||
is_used: true
|
||||
is_mut: expr.is_mut
|
||||
sum_type_casts: sum_type_casts
|
||||
smartcasts: smartcasts
|
||||
orig_type: orig_type
|
||||
})
|
||||
}
|
||||
@ -5378,29 +5377,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||
}
|
||||
if is_variable {
|
||||
if left_sym.kind in [.interface_, .sum_type] {
|
||||
if branch.cond.left is ast.Ident && left_sym.kind == .interface_ {
|
||||
// TODO: rewrite interface smartcast
|
||||
left := branch.cond.left as ast.Ident
|
||||
mut is_mut := false
|
||||
mut sum_type_casts := []ast.Type{}
|
||||
if v := branch.scope.find_var(left.name) {
|
||||
is_mut = v.is_mut
|
||||
sum_type_casts << v.sum_type_casts
|
||||
}
|
||||
branch.scope.register(ast.Var{
|
||||
name: left.name
|
||||
typ: right_expr.typ.to_ptr()
|
||||
sum_type_casts: sum_type_casts
|
||||
pos: left.pos
|
||||
is_used: true
|
||||
is_mut: is_mut
|
||||
})
|
||||
// TODO: needs to be removed
|
||||
node.branches[i].smartcast = true
|
||||
} else {
|
||||
c.smartcast_sumtype(branch.cond.left, branch.cond.left_type,
|
||||
right_expr.typ, mut branch.scope)
|
||||
}
|
||||
c.smartcast(branch.cond.left, branch.cond.left_type, right_expr.typ, mut
|
||||
branch.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/no_method_on_interface_propagation.vv:18:5: error: unknown method: `Cat.foo`
|
||||
16 | mut a := new_animal('persian')
|
||||
16 | a := new_animal('persian')
|
||||
17 | if a is Cat {
|
||||
18 | a.foo()
|
||||
| ~~~~~
|
||||
|
@ -13,7 +13,7 @@ fn new_animal(breed string) Animal {
|
||||
}
|
||||
|
||||
fn test_methods_on_interfaces_dont_exist_on_implementers() {
|
||||
mut a := new_animal('persian')
|
||||
a := new_animal('persian')
|
||||
if a is Cat {
|
||||
a.foo()
|
||||
}
|
||||
|
@ -644,9 +644,9 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam
|
||||
deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
|
||||
value_fmt := if typ == ast.string_type { "'%.*s\\000'" } else { '%.*s\\000' }
|
||||
|
||||
g.auto_str_funcs.write_string('\tif (x._interface_idx == _${styp}_${subtype.cname}_index)')
|
||||
g.auto_str_funcs.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
|
||||
g.auto_str_funcs.write_string(' return _STR("${clean_interface_v_type_name}($value_fmt)", 2, ')
|
||||
g.auto_str_funcs.write_string('${func_name}(${deref}($subtype.cname*)x._object')
|
||||
g.auto_str_funcs.write_string('${func_name}(${deref}($subtype.cname*)x._$subtype.cname')
|
||||
if should_use_indent_func(subtype.kind) && !sym_has_str_method {
|
||||
g.auto_str_funcs.write_string(', indent_count')
|
||||
}
|
||||
|
@ -824,14 +824,20 @@ static inline void __${typ.cname}_pushval($typ.cname ch, $el_stype val) {
|
||||
pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) {
|
||||
info := sym.info as ast.Interface
|
||||
g.type_definitions.writeln('typedef struct {')
|
||||
g.type_definitions.writeln('\tvoid* _object;')
|
||||
g.type_definitions.writeln('\tint _interface_idx;')
|
||||
g.type_definitions.writeln('\tunion {')
|
||||
g.type_definitions.writeln('\t\tvoid* _object;')
|
||||
for variant in info.types {
|
||||
vcname := g.table.get_type_symbol(variant).cname
|
||||
g.type_definitions.writeln('\t\t$vcname* _$vcname;')
|
||||
}
|
||||
g.type_definitions.writeln('\t};')
|
||||
g.type_definitions.writeln('\tint _typ;')
|
||||
for field in info.fields {
|
||||
styp := g.typ(field.typ)
|
||||
cname := c_name(field.name)
|
||||
g.type_definitions.writeln('\t$styp* $cname;')
|
||||
}
|
||||
g.type_definitions.writeln('} ${c_name(sym.name)};\n')
|
||||
g.type_definitions.writeln('} ${c_name(sym.name)};')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
|
||||
@ -1703,7 +1709,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
||||
scope := g.file.scope.innermost(expr.position().pos)
|
||||
if expr is ast.Ident {
|
||||
if v := scope.find_var(expr.name) {
|
||||
if v.sum_type_casts.len > 0 {
|
||||
if v.smartcasts.len > 0 {
|
||||
is_already_sum_type = true
|
||||
}
|
||||
}
|
||||
@ -3224,7 +3230,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||
mut sum_type_dot := '.'
|
||||
if f := g.table.find_field(sym, node.field_name) {
|
||||
field_sym := g.table.get_type_symbol(f.typ)
|
||||
if field_sym.kind == .sum_type {
|
||||
if field_sym.kind in [.sum_type, .interface_] {
|
||||
if !prevent_sum_type_unwrapping_once {
|
||||
// check first if field is sum type because scope searching is expensive
|
||||
scope := g.file.scope.innermost(node.pos.pos)
|
||||
@ -3232,9 +3238,11 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||
if field.orig_type.is_ptr() {
|
||||
sum_type_dot = '->'
|
||||
}
|
||||
// union sum type deref
|
||||
for i, typ in field.sum_type_casts {
|
||||
g.write('(*')
|
||||
for i, typ in field.smartcasts {
|
||||
g.write('(')
|
||||
if field_sym.kind == .sum_type {
|
||||
g.write('*')
|
||||
}
|
||||
cast_sym := g.table.get_type_symbol(typ)
|
||||
if i != 0 {
|
||||
dot := if field.typ.is_ptr() { '->' } else { '.' }
|
||||
@ -3966,9 +3974,9 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
if branch.exprs[sumtype_index] is ast.TypeNode {
|
||||
typ := branch.exprs[sumtype_index] as ast.TypeNode
|
||||
branch_sym := g.table.get_type_symbol(typ.typ)
|
||||
g.write('${dot_or_ptr}_interface_idx == _${sym.cname}_${branch_sym.cname}_index')
|
||||
g.write('${dot_or_ptr}_typ == _${sym.cname}_${branch_sym.cname}_index')
|
||||
} else if branch.exprs[sumtype_index] is ast.None && sym.name == 'IError' {
|
||||
g.write('${dot_or_ptr}_interface_idx == _IError_None___index')
|
||||
g.write('${dot_or_ptr}_typ == _IError_None___index')
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
@ -4323,12 +4331,16 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
}
|
||||
scope := g.file.scope.innermost(node.pos.pos)
|
||||
if v := scope.find_var(node.name) {
|
||||
if v.sum_type_casts.len > 0 {
|
||||
if v.smartcasts.len > 0 {
|
||||
v_sym := g.table.get_type_symbol(v.typ)
|
||||
if !prevent_sum_type_unwrapping_once {
|
||||
for _ in v.sum_type_casts {
|
||||
g.write('(*')
|
||||
for _ in v.smartcasts {
|
||||
g.write('(')
|
||||
if v_sym.kind == .sum_type {
|
||||
g.write('*')
|
||||
}
|
||||
}
|
||||
for i, typ in v.sum_type_casts {
|
||||
for i, typ in v.smartcasts {
|
||||
cast_sym := g.table.get_type_symbol(typ)
|
||||
mut is_ptr := false
|
||||
if i == 0 {
|
||||
@ -4576,23 +4588,6 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if branch.smartcast && branch.stmts.len > 0 {
|
||||
infix := branch.cond as ast.InfixExpr
|
||||
if mut infix.left is ast.Ident {
|
||||
right_type := infix.right as ast.TypeNode
|
||||
left_type := infix.left_type
|
||||
it_type := g.typ(right_type.typ)
|
||||
g.write('\t$it_type* _sc_tmp_$branch.pos.pos = ($it_type*)')
|
||||
g.expr(infix.left)
|
||||
if left_type.is_ptr() {
|
||||
g.write('->')
|
||||
} else {
|
||||
g.write('.')
|
||||
}
|
||||
g.writeln('_object;')
|
||||
g.writeln('\t$it_type* $infix.left.name = _sc_tmp_$branch.pos.pos;')
|
||||
}
|
||||
}
|
||||
if needs_tmp_var {
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp)
|
||||
} else {
|
||||
@ -5602,7 +5597,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
|
||||
is_none_ok := mr_styp == 'void'
|
||||
g.writeln(';')
|
||||
if is_none_ok {
|
||||
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._interface_idx != _IError_None___index) {')
|
||||
g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
|
||||
} else {
|
||||
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
|
||||
}
|
||||
@ -6115,7 +6110,7 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) {
|
||||
}
|
||||
sym := g.table.get_type_symbol(node.left_type)
|
||||
if sym.kind == .interface_ {
|
||||
g.write('_interface_idx $eq ')
|
||||
g.write('_typ $eq ')
|
||||
// `_Animal_Dog_index`
|
||||
sub_type := match mut node.right {
|
||||
ast.TypeNode { node.right.typ }
|
||||
@ -6207,8 +6202,8 @@ fn (mut g Gen) interface_table() string {
|
||||
sb.writeln('$staticprefix $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x);')
|
||||
mut cast_struct := strings.new_builder(100)
|
||||
cast_struct.writeln('($interface_name) {')
|
||||
cast_struct.writeln('\t\t._object = (void*) (x),')
|
||||
cast_struct.writeln('\t\t._interface_idx = $interface_index_name,')
|
||||
cast_struct.writeln('\t\t._$cctype = x,')
|
||||
cast_struct.writeln('\t\t._typ = $interface_index_name,')
|
||||
for field in inter_info.fields {
|
||||
cname := c_name(field.name)
|
||||
field_styp := g.typ(field.typ)
|
||||
|
@ -195,7 +195,7 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
|
||||
// if g.pref.show_cc && it.is_builtin {
|
||||
// println(name)
|
||||
// }
|
||||
// type_name := g.table.Type_to_str(it.return_type)
|
||||
// type_name := g.ast.Type_to_str(it.return_type)
|
||||
// Live functions are protected by a mutex, because otherwise they
|
||||
// can be changed by the live reload thread, *while* they are
|
||||
// running, with unpredictable results (usually just crashing).
|
||||
@ -494,7 +494,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||
g.expr(node.left)
|
||||
dot := if node.left_type.is_ptr() { '->' } else { '.' }
|
||||
mname := c_name(node.name)
|
||||
g.write('${dot}_interface_idx]._method_${mname}(')
|
||||
g.write('${dot}_typ]._method_${mname}(')
|
||||
g.expr(node.left)
|
||||
g.write('${dot}_object')
|
||||
if node.args.len > 0 {
|
||||
@ -578,7 +578,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||
g.write('tos3( /* $left_sym.name */ v_typeof_interface_${typ_sym.cname}( (')
|
||||
g.expr(node.left)
|
||||
dot := if node.left_type.is_ptr() { '->' } else { '.' }
|
||||
g.write(')${dot}_interface_idx ))')
|
||||
g.write(')${dot}_typ ))')
|
||||
return
|
||||
}
|
||||
if node.name == 'str' {
|
||||
|
Loading…
Reference in New Issue
Block a user