checker: check interface implementation
parent
b627bb933c
commit
215657e16a
|
@ -395,12 +395,25 @@ pub fn (mut c Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
|
|||
if left.kind == .array {
|
||||
// `array << elm`
|
||||
c.fail_if_immutable(infix_expr.left)
|
||||
left_value_type := c.table.value_type(left_type)
|
||||
left_value_sym := c.table.get_type_symbol(left_value_type)
|
||||
if left_value_sym.kind == .interface_ {
|
||||
if right.kind != .array {
|
||||
// []Animal << Cat
|
||||
c.type_implements(right_type, left_value_type, infix_expr.right.position())
|
||||
} else {
|
||||
// []Animal << Cat
|
||||
c.type_implements(c.table.value_type(right_type), left_value_type,
|
||||
infix_expr.right.position())
|
||||
}
|
||||
return table.void_type
|
||||
}
|
||||
// the expressions have different types (array_x and x)
|
||||
if c.table.check(right_type, c.table.value_type(left_type)) { // , right_type) {
|
||||
if c.table.check(right_type, left_value_type) { // , right_type) {
|
||||
// []T << T
|
||||
return table.void_type
|
||||
}
|
||||
if right.kind == .array && c.table.check(c.table.value_type(left_type), c.table.value_type(right_type)) {
|
||||
if right.kind == .array && c.table.check(left_value_type, c.table.value_type(right_type)) {
|
||||
// []T << []T
|
||||
return table.void_type
|
||||
}
|
||||
|
@ -665,6 +678,7 @@ pub fn (mut c Checker) call_method(call_expr mut ast.CallExpr) table.Type {
|
|||
for i, arg in call_expr.args {
|
||||
exp_arg_typ := if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len -
|
||||
1].typ } else { method.args[i + 1].typ }
|
||||
exp_arg_sym := c.table.get_type_symbol(exp_arg_typ)
|
||||
c.expected_type = exp_arg_typ
|
||||
got_arg_typ := c.expr(arg.expr)
|
||||
call_expr.args[i].typ = got_arg_typ
|
||||
|
@ -672,9 +686,12 @@ pub fn (mut c Checker) call_method(call_expr mut ast.CallExpr) table.Type {
|
|||
1 > i {
|
||||
c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos)
|
||||
}
|
||||
if exp_arg_sym.kind == .interface_ {
|
||||
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
|
||||
continue
|
||||
}
|
||||
if !c.table.check(got_arg_typ, exp_arg_typ) {
|
||||
got_arg_sym := c.table.get_type_symbol(got_arg_typ)
|
||||
exp_arg_sym := c.table.get_type_symbol(exp_arg_typ)
|
||||
// str method, allow type with str method if fn arg is string
|
||||
if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') {
|
||||
continue
|
||||
|
@ -831,6 +848,17 @@ pub fn (mut c Checker) call_fn(call_expr mut ast.CallExpr) table.Type {
|
|||
if f.is_variadic && typ.flag_is(.variadic) && call_expr.args.len - 1 > i {
|
||||
c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos)
|
||||
}
|
||||
// Handle expected interface
|
||||
if arg_typ_sym.kind == .interface_ {
|
||||
c.type_implements(typ, arg.typ, call_arg.expr.position())
|
||||
continue
|
||||
}
|
||||
// Handle expected interface array
|
||||
/*
|
||||
if exp_type_sym.kind == .array && t.get_type_symbol(t.value_type(exp_idx)).kind == .interface_ {
|
||||
return true
|
||||
}
|
||||
*/
|
||||
if !c.table.check(typ, arg.typ) {
|
||||
// str method, allow type with str method if fn arg is string
|
||||
if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
||||
|
@ -844,7 +872,6 @@ pub fn (mut c Checker) call_fn(call_expr mut ast.CallExpr) table.Type {
|
|||
}
|
||||
if typ_sym.kind == .array_fixed {
|
||||
}
|
||||
// println('fixed')
|
||||
c.error('cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`',
|
||||
call_expr.pos)
|
||||
}
|
||||
|
@ -852,6 +879,25 @@ pub fn (mut c Checker) call_fn(call_expr mut ast.CallExpr) table.Type {
|
|||
return f.return_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position) {
|
||||
typ_sym := c.table.get_type_symbol(typ)
|
||||
inter_sym := c.table.get_type_symbol(inter_typ)
|
||||
styp := c.table.type_to_str(typ)
|
||||
for imethod in inter_sym.methods {
|
||||
if method := typ_sym.find_method(imethod.name) {
|
||||
if !imethod.is_same_method_as(method) {
|
||||
c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`, expected `${c.table.fn_to_str(imethod)}`', pos)
|
||||
}
|
||||
continue
|
||||
}
|
||||
c.error("`$styp` doesn't implement method `$imethod.name`", pos)
|
||||
}
|
||||
mut inter_info := inter_sym.info as table.Interface
|
||||
if typ !in inter_info.types && typ_sym.kind != .interface_ {
|
||||
inter_info.types << typ
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) check_expr_opt_call(x ast.Expr, xtype table.Type, is_return_used bool) {
|
||||
match x {
|
||||
ast.CallExpr {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_a.v:10:6: error: `Cat` doesn't implement method `name`
|
||||
8 |
|
||||
9 | fn main() {
|
||||
10 | foo(Cat{})
|
||||
| ~~~~~
|
||||
11 | }
|
|
@ -0,0 +1,11 @@
|
|||
interface Animal {
|
||||
name() string
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn foo(a Animal) {}
|
||||
|
||||
fn main() {
|
||||
foo(Cat{})
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_b.v:13:6: error: `Cat` incorrectly implements method `name` of interface `Animal`, expected `name() string`
|
||||
11 | fn main() {
|
||||
12 | c := Cat{}
|
||||
13 | foo(c)
|
||||
| ^
|
||||
14 | }
|
|
@ -0,0 +1,14 @@
|
|||
interface Animal {
|
||||
name() string
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) name() {}
|
||||
|
||||
fn foo(a Animal) {}
|
||||
|
||||
fn main() {
|
||||
c := Cat{}
|
||||
foo(c)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_c.v:12:6: error: `Cat` incorrectly implements method `name` of interface `Animal`, expected `name()`
|
||||
10 |
|
||||
11 | fn main() {
|
||||
12 | foo(Cat{})
|
||||
| ~~~~~
|
||||
13 | }
|
|
@ -0,0 +1,13 @@
|
|||
interface Animal {
|
||||
name()
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) name(s string) {}
|
||||
|
||||
fn foo(a Animal) {}
|
||||
|
||||
fn main() {
|
||||
foo(Cat{})
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_d.v:12:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`, expected `speak(s string)`
|
||||
10 |
|
||||
11 | fn main() {
|
||||
12 | foo(Cat{})
|
||||
| ~~~~~
|
||||
13 | }
|
|
@ -0,0 +1,13 @@
|
|||
interface Animal {
|
||||
speak(s string)
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) speak() {}
|
||||
|
||||
fn foo(a Animal) {}
|
||||
|
||||
fn main() {
|
||||
foo(Cat{})
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_e.v:12:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`, expected `speak(s string)`
|
||||
10 |
|
||||
11 | fn main() {
|
||||
12 | foo(Cat{})
|
||||
| ~~~~~
|
||||
13 | }
|
|
@ -0,0 +1,13 @@
|
|||
interface Animal {
|
||||
speak(s string)
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) speak(s &string) {}
|
||||
|
||||
fn foo(a Animal) {}
|
||||
|
||||
fn main() {
|
||||
foo(Cat{})
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_f.v:11:13: error: `Cat` incorrectly implements method `speak` of interface `Animal`, expected `speak(s string)`
|
||||
9 | fn main() {
|
||||
10 | mut animals := []Animal{}
|
||||
11 | animals << Cat{}
|
||||
| ~~~~~
|
||||
12 | }
|
|
@ -0,0 +1,12 @@
|
|||
interface Animal {
|
||||
speak(s string)
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) speak() {}
|
||||
|
||||
fn main() {
|
||||
mut animals := []Animal{}
|
||||
animals << Cat{}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_g.v:12:13: error: `Cat` incorrectly implements method `speak` of interface `Animal`, expected `speak(s string)`
|
||||
10 | mut animals := []Animal{}
|
||||
11 | mut cats := []Cat{}
|
||||
12 | animals << cats
|
||||
| ~~~~
|
||||
13 | }
|
||||
14 |
|
|
@ -0,0 +1,14 @@
|
|||
interface Animal {
|
||||
speak(s string)
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn (c Cat) speak() {}
|
||||
|
||||
fn main() {
|
||||
mut animals := []Animal{}
|
||||
mut cats := []Cat{}
|
||||
animals << cats
|
||||
}
|
||||
|
|
@ -641,6 +641,26 @@ pub fn (table &Table) type_to_str(t Type) string {
|
|||
return res
|
||||
}
|
||||
|
||||
pub fn(t &Table) fn_to_str(func &Fn) string {
|
||||
mut sb := strings.new_builder(20)
|
||||
sb.write('${func.name}(')
|
||||
for i in 1 .. func.args.len {
|
||||
arg := func.args[i]
|
||||
sb.write('$arg.name')
|
||||
if i == func.args.len - 1 || func.args[i + 1].typ != arg.typ {
|
||||
sb.write(' ${t.type_to_str(arg.typ)}')
|
||||
}
|
||||
if i != func.args.len - 1 {
|
||||
sb.write(', ')
|
||||
}
|
||||
}
|
||||
sb.write(')')
|
||||
if func.return_type != table.void_type {
|
||||
sb.write(' ${t.type_to_str(func.return_type)}')
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) has_method(name string) bool {
|
||||
t.find_method(name) or {
|
||||
return false
|
||||
|
|
|
@ -71,6 +71,21 @@ pub fn (f &Fn) signature() string {
|
|||
return sig
|
||||
}
|
||||
|
||||
pub fn (f &Fn) is_same_method_as(func &Fn) bool {
|
||||
if f.return_type != func.return_type {
|
||||
return false
|
||||
}
|
||||
if f.args.len != func.args.len {
|
||||
return false
|
||||
}
|
||||
for i in 1 .. f.args.len {
|
||||
if f.args[i].typ != func.args[i].typ {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (t &Table) find_fn(name string) ?Fn {
|
||||
f := t.fns[name]
|
||||
if f.name.str != 0 {
|
||||
|
@ -147,15 +162,6 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
|
|||
return none
|
||||
}
|
||||
|
||||
pub fn (t &Table) interface_add_type(inter mut Interface, typ Type) bool {
|
||||
// TODO Verify `typ` implements `inter`
|
||||
typ_sym := t.get_type_symbol(typ)
|
||||
if typ !in inter.types && typ_sym.kind != .interface_ {
|
||||
inter.types << typ
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (t &Table) find_type_idx(name string) int {
|
||||
return t.type_idxs[name]
|
||||
|
@ -470,17 +476,6 @@ pub fn (t &Table) check(got, expected Type) bool {
|
|||
// # NOTE: use symbols from this point on for perf
|
||||
got_type_sym := t.get_type_symbol(got)
|
||||
exp_type_sym := t.get_type_symbol(expected)
|
||||
// Handle expected interface
|
||||
if exp_type_sym.kind == .interface_ {
|
||||
mut info := exp_type_sym.info as Interface
|
||||
return t.interface_add_type(info, got)
|
||||
}
|
||||
// Handle expected interface array
|
||||
/*
|
||||
if exp_type_sym.kind == .array && t.get_type_symbol(t.value_type(exp_idx)).kind == .interface_ {
|
||||
return true
|
||||
}
|
||||
*/
|
||||
//
|
||||
if exp_type_sym.kind == .function && got_type_sym.kind == .int {
|
||||
// TODO temporary
|
||||
|
|
|
@ -137,18 +137,14 @@ interface Animal {
|
|||
speak(s string)
|
||||
}
|
||||
|
||||
// utility function to convert to string, as a sample
|
||||
fn (a Animal) str() string {
|
||||
return 'Animal: type:${typeof(a)}, name:' + a.name() + '.'
|
||||
}
|
||||
|
||||
fn test_interface_array() {
|
||||
println('Test on array of animals ...')
|
||||
mut animals := []Animal{}
|
||||
animals = [ Cat{}, Dog{breed: 'Labrador Retriever'} ]
|
||||
animals << Cat{}
|
||||
assert true
|
||||
println('Animals array contains: ${animals.str()}') // explicit call to 'str' function
|
||||
println('Animals array contains: ${animals}') // implicit call to 'str' function
|
||||
// TODO .str() from the real types should be called
|
||||
// println('Animals array contains: ${animals.str()}') // explicit call to 'str' function
|
||||
// println('Animals array contains: ${animals}') // implicit call to 'str' function
|
||||
assert animals.len == 3
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue