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 {
|
if left.kind == .array {
|
||||||
// `array << elm`
|
// `array << elm`
|
||||||
c.fail_if_immutable(infix_expr.left)
|
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)
|
// 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
|
// []T << T
|
||||||
return table.void_type
|
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
|
// []T << []T
|
||||||
return table.void_type
|
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 {
|
for i, arg in call_expr.args {
|
||||||
exp_arg_typ := if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len -
|
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 }
|
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
|
c.expected_type = exp_arg_typ
|
||||||
got_arg_typ := c.expr(arg.expr)
|
got_arg_typ := c.expr(arg.expr)
|
||||||
call_expr.args[i].typ = got_arg_typ
|
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 {
|
1 > i {
|
||||||
c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos)
|
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) {
|
if !c.table.check(got_arg_typ, exp_arg_typ) {
|
||||||
got_arg_sym := c.table.get_type_symbol(got_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
|
// str method, allow type with str method if fn arg is string
|
||||||
if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') {
|
if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') {
|
||||||
continue
|
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 {
|
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)
|
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) {
|
if !c.table.check(typ, arg.typ) {
|
||||||
// str method, allow type with str method if fn arg is string
|
// str method, allow type with str method if fn arg is string
|
||||||
if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
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 {
|
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`',
|
c.error('cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`',
|
||||||
call_expr.pos)
|
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
|
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) {
|
pub fn (mut c Checker) check_expr_opt_call(x ast.Expr, xtype table.Type, is_return_used bool) {
|
||||||
match x {
|
match x {
|
||||||
ast.CallExpr {
|
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
|
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 {
|
pub fn (t &TypeSymbol) has_method(name string) bool {
|
||||||
t.find_method(name) or {
|
t.find_method(name) or {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -71,6 +71,21 @@ pub fn (f &Fn) signature() string {
|
||||||
return sig
|
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 {
|
pub fn (t &Table) find_fn(name string) ?Fn {
|
||||||
f := t.fns[name]
|
f := t.fns[name]
|
||||||
if f.name.str != 0 {
|
if f.name.str != 0 {
|
||||||
|
@ -147,15 +162,6 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
|
||||||
return none
|
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]
|
[inline]
|
||||||
pub fn (t &Table) find_type_idx(name string) int {
|
pub fn (t &Table) find_type_idx(name string) int {
|
||||||
return t.type_idxs[name]
|
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
|
// # NOTE: use symbols from this point on for perf
|
||||||
got_type_sym := t.get_type_symbol(got)
|
got_type_sym := t.get_type_symbol(got)
|
||||||
exp_type_sym := t.get_type_symbol(expected)
|
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 {
|
if exp_type_sym.kind == .function && got_type_sym.kind == .int {
|
||||||
// TODO temporary
|
// TODO temporary
|
||||||
|
|
|
@ -137,18 +137,14 @@ interface Animal {
|
||||||
speak(s string)
|
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() {
|
fn test_interface_array() {
|
||||||
println('Test on array of animals ...')
|
println('Test on array of animals ...')
|
||||||
mut animals := []Animal{}
|
mut animals := []Animal{}
|
||||||
animals = [ Cat{}, Dog{breed: 'Labrador Retriever'} ]
|
animals = [ Cat{}, Dog{breed: 'Labrador Retriever'} ]
|
||||||
animals << Cat{}
|
animals << Cat{}
|
||||||
assert true
|
assert true
|
||||||
println('Animals array contains: ${animals.str()}') // explicit call to 'str' function
|
// TODO .str() from the real types should be called
|
||||||
println('Animals array contains: ${animals}') // implicit call to 'str' function
|
// 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
|
assert animals.len == 3
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue