generics: fix method calls

pull/5086/head
Alexander Medvednikov 2020-05-28 05:50:57 +02:00
parent ae8cc2f433
commit d6037cbcf2
4 changed files with 54 additions and 39 deletions

View File

@ -366,7 +366,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`',
field.pos)
}
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && !expr_type.is_number() {
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() &&
!expr_type.is_number() {
c.error('ref', field.pos)
}
struct_init.fields[i].typ = expr_type
@ -468,17 +469,18 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
if infix_expr.op in [.mod, .xor, .amp, .pipe] {
side := if left_type == promoted_type { 'left' } else { 'right' }
pos := if left_type == promoted_type { left_pos } else { right_pos }
name := if left_type == promoted_type { left.name } else { right.name }
name := if left_type == promoted_type { left.name } else { right.name }
if infix_expr.op == .mod {
c.error('float modulo not allowed, use math.fmod() instead', pos)
} else {
c.error('$side type of `${infix_expr.op.str()}` cannot be non-integer type $name', pos)
c.error('$side type of `${infix_expr.op.str()}` cannot be non-integer type $name',
pos)
}
}
}
if infix_expr.op in [.div, .mod] {
if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == '0' ||
infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0 {
if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() ==
'0' || infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0 {
oper := if infix_expr.op == .div { 'division' } else { 'modulo' }
c.error('$oper by zero', right_pos)
}
@ -498,7 +500,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
c.type_implements(right_type, left_value_type, right_pos)
} else {
// []Animal << Cat
c.type_implements(c.table.value_type(right_type), left_value_type, right_pos)
c.type_implements(c.table.value_type(right_type), left_value_type,
right_pos)
}
return table.void_type
}
@ -700,6 +703,7 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
left_type := c.expr(call_expr.left)
is_generic := left_type == table.t_type
call_expr.left_type = left_type
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
method_name := call_expr.name
@ -803,7 +807,12 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
call_expr.expected_arg_types << method.args[i].typ
}
}
call_expr.receiver_type = method.args[0].typ
if is_generic {
// We need the receiver to be T in cgen.
call_expr.receiver_type = table.t_type
} else {
call_expr.receiver_type = method.args[0].typ
}
call_expr.return_type = method.return_type
return method.return_type
}
@ -1167,9 +1176,8 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
mut got_types := []table.Type{}
for expr in return_stmt.exprs {
typ := c.expr(expr)
// Unpack multi return types
sym := c.table.get_type_symbol(typ)
sym := c.table.get_type_symbol(typ)
if sym.kind == .multi_return {
for t in sym.mr_info().types {
got_types << t
@ -1183,12 +1191,10 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
if exp_is_optional && got_types[0].idx() in [table.none_type_idx, c.table.type_idxs['Option']] {
return
}
if expected_types.len > 0 && expected_types.len != got_types.len {
c.error('wrong number of return arguments', return_stmt.pos)
return
}
for i, exp_type in expected_types {
got_typ := got_types[i]
is_generic := exp_type == table.t_type
@ -1875,10 +1881,10 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
// second use
if ident.kind == .variable {
info := ident.info as ast.IdentVar
if info.typ == table.t_type {
// Got a var with type T, return current generic type
// return c.cur_generic_type
}
// if info.typ == table.t_type {
// Got a var with type T, return current generic type
// return c.cur_generic_type
// }
return info.typ
} else if ident.kind == .constant {
info := ident.info as ast.IdentVar
@ -2183,7 +2189,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
}
}
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
node.pos)
node.pos)
}
} else {
c.error('`if` expression requires an expression as the last statement of every branch',
@ -2194,7 +2200,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
// if only untyped literals were given default to int/f64
if node.typ == table.any_int_type {
node.typ = table.int_type
} else if node.typ == table.any_flt_type {
} else if node.typ == table.any_flt_type {
node.typ = table.f64_type
}
if expr_required {
@ -2410,8 +2416,8 @@ fn (mut c Checker) fn_decl(it ast.FnDecl) {
// loop thru each generic type and generate a function
for gen_type in c.table.fn_gen_types[it.name] {
c.cur_generic_type = gen_type
//sym:=c.table.get_type_symbol(gen_type)
//println('\ncalling check for $it.name for type $sym.name')
// sym:=c.table.get_type_symbol(gen_type)
// println('\ncalling check for $it.name for type $sym.name')
c.fn_decl(it)
}
c.cur_generic_type = 0

View File

@ -8,7 +8,9 @@ import v.table
import strings
const (
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t']
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t',
'\t\t\t\t\t\t\t\t'
]
max_len = 90
)
@ -405,7 +407,8 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) {
}
f.write(')')
if fn_info.return_type.idx() != table.void_type_idx {
ret_str := f.table.type_to_str(fn_info.return_type).replace(f.cur_mod + '.', '')
ret_str := f.table.type_to_str(fn_info.return_type).replace(f.cur_mod + '.',
'')
f.write(' ' + ret_str)
}
}
@ -592,15 +595,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
if it.keys.len == 0 {
mut ktyp := it.key_type
mut vtyp := it.value_type
if vtyp == 0 {
typ_sym := f.table.get_type_symbol(it.typ)
minfo := typ_sym.info as table.Map
ktyp = minfo.key_type
vtyp = minfo.value_type
}
f.write('map[')
f.write(f.type_to_str(ktyp))
f.write(']')

View File

@ -48,19 +48,19 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
'irq_handler' {
g.write('__IRQHANDLER ')
}
// GCC/clang attributes
// prefixed by _ to indicate they're for advanced users only and not really supported by V.
// source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
// The cold attribute on functions is used to inform the compiler that the function is unlikely
// The cold attribute on functions is used to inform the compiler that the function is unlikely
// to be executed. The function is optimized for size rather than speed and on many targets it
// is placed into a special subsection of the text section so all cold functions appear close
// together, improving code locality of non-cold parts of program.
'_cold' {
g.write('__attribute__((cold)) ')
}
// The constructor attribute causes the function to be called automatically before execution
// The constructor attribute causes the function to be called automatically before execution
// enters main ().
'_constructor' {
g.write('__attribute__((constructor)) ')
@ -70,7 +70,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
'_destructor' {
g.write('__attribute__((destructor)) ')
}
// Generally, inlining into a function is limited. For a function marked with this attribute,
// Generally, inlining into a function is limited. For a function marked with this attribute,
// every call inside this function is inlined, if possible.
'_flatten' {
g.write('__attribute__((flatten)) ')
@ -80,16 +80,16 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
'_hot' {
g.write('__attribute__((hot)) ')
}
// This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by
// the function cannot alias any other pointer valid when the function returns, and moreover no
// This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by
// the function cannot alias any other pointer valid when the function returns, and moreover no
// pointers to valid objects occur in any storage addressed by P.
'_malloc' {
g.write('__attribute__((malloc)) ')
}
// Calls to functions whose return value is not affected by changes to the observable state
// of the program and that have no observable effects on such state other than to return a
// value may lend themselves to optimizations such as common subexpression elimination.
// Calls to functions whose return value is not affected by changes to the observable state
// of the program and that have no observable effects on such state other than to return a
// value may lend themselves to optimizations such as common subexpression elimination.
// Declaring such functions with the const attribute allows GCC to avoid emitting some calls in
// repeated invocations of the function with the same argument values.
'_pure' {
@ -380,6 +380,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
}
}
pub fn (mut g Gen) unwrap_generic(typ table.Type) table.Type {
if typ == table.t_type {
return g.cur_generic_type
}
return typ
}
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 {
@ -387,7 +394,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
}
// mut receiver_type_name := g.cc_type(node.receiver_type)
// mut receiver_type_name := g.typ(node.receiver_type)
typ_sym := g.table.get_type_symbol(node.receiver_type)
typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type))
mut receiver_type_name := typ_sym.name.replace('.', '__')
if typ_sym.kind == .interface_ {
// Speaker_name_table[s._interface_idx].speak(s._object)

View File

@ -40,10 +40,11 @@ fn test_foo() {
fn create<T>() {
a := T{}
mut b := T{}
b.foo = 'foo'
println(b.foo)
assert b.foo == 'foo'
mut xx := T{}
xx.foo = 'foo'
println(xx.foo)
assert xx.foo == 'foo'
xx.init()
}
struct User {
@ -65,6 +66,7 @@ fn (c City) init() {
fn test_create() {
create<User>()
create<City>()
// create<User>()
}
/*