ast.table: cleanup type_implements_interface() (#10643)

pull/10646/head
yuyi 2021-07-02 15:18:04 +08:00 committed by GitHub
parent a33a2ba095
commit abbf71c794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 112 deletions

View File

@ -1099,9 +1099,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
}
}
fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
// TODO: merge with c.type_implements, which also does error reporting in addition
// to checking.
pub fn (mut t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
utyp := typ
if utyp.idx() == inter_typ.idx() {
// same type -> already casted to the interface
@ -1111,40 +1109,44 @@ fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) boo
// `none` "implements" the Error interface
return true
}
typ_sym := table.get_type_symbol(utyp)
typ_sym := t.get_type_symbol(utyp)
if typ_sym.language != .v {
return false
}
mut inter_sym := table.get_type_symbol(inter_typ)
mut inter_sym := t.get_type_symbol(inter_typ)
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
return false
}
// do not check the same type more than once
if mut inter_sym.info is Interface {
for t in inter_sym.info.types {
if t.idx() == utyp.idx() {
// do not check the same type more than once
for tt in inter_sym.info.types {
if tt.idx() == utyp.idx() {
return true
}
}
}
imethods := if inter_sym.kind == .interface_ {
(inter_sym.info as Interface).methods
} else {
inter_sym.methods
}
for imethod in imethods {
if method := typ_sym.find_method(imethod.name) {
msg := table.is_same_method(imethod, method)
if msg.len > 0 {
return false
// verify methods
for imethod in inter_sym.info.methods {
if method := typ_sym.find_method(imethod.name) {
msg := t.is_same_method(imethod, method)
if msg.len > 0 {
return false
}
continue
}
continue
return false
}
return false
}
if mut inter_sym.info is Interface {
// verify fields
for ifield in inter_sym.info.fields {
if field := table.find_field_with_embeds(typ_sym, ifield.name) {
if ifield.typ == voidptr_type {
// Allow `voidptr` fields in interfaces for now. (for example
// to enable .db check in vweb)
if t.struct_has_field(typ_sym, ifield.name) {
continue
} else {
return false
}
}
if field := t.find_field_with_embeds(typ_sym, ifield.name) {
if ifield.typ != field.typ {
return false
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
@ -1158,8 +1160,9 @@ fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) boo
if !inter_sym.info.types.contains(voidptr_type) {
inter_sym.info.types << voidptr_type
}
return true
}
return true
return false
}
// resolve_generic_to_concrete resolves generics to real types T => int.
@ -1325,88 +1328,3 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
}
}
}
pub fn (mut table Table) type_implements_interface(utyp Type, interface_type Type) bool {
$if debug_interface_type_implements ? {
eprintln('> Table.type_implements_inteface typ: $utyp.debug() | interface_typ: $interface_type.debug()')
}
// utyp := c.unwrap_generic(typ)
typ_sym := table.get_type_symbol(utyp)
mut inter_sym := table.get_type_symbol(interface_type)
// do not check the same type more than once
if mut inter_sym.info is Interface {
for t in inter_sym.info.types {
if t.idx() == utyp.idx() {
return true
}
}
}
// styp := table.type_to_str(utyp)
if utyp.idx() == interface_type.idx() {
// same type -> already casted to the interface
return true
}
if interface_type.idx() == error_type_idx && utyp.idx() == none_type_idx {
// `none` "implements" the Error interface
return true
}
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
return false
// error('cannot implement interface `$inter_sym.name` with a different interface `$styp`',
// pos)
}
imethods := if inter_sym.kind == .interface_ {
(inter_sym.info as Interface).methods
} else {
inter_sym.methods
}
// Verify methods
for imethod in imethods {
if method := typ_sym.find_method(imethod.name) {
msg := table.is_same_method(imethod, method)
if msg.len > 0 {
// sig := table.fn_signature(imethod, skip_receiver: true)
// add_error_detail('$inter_sym.name has `$sig`')
// c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`: $msg',
// pos)
return false
}
continue
}
// c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
// pos)
return false
}
// Verify fields
if mut inter_sym.info is Interface {
for ifield in inter_sym.info.fields {
if ifield.typ == voidptr_type {
// Allow `voidptr` fields in interfaces for now. (for example
// to enable .db check in vweb)
if table.struct_has_field(typ_sym, ifield.name) {
continue
} else {
return false
}
}
if field := table.find_field_with_embeds(typ_sym, ifield.name) {
if ifield.typ != field.typ {
// exp := table.type_to_str(ifield.typ)
// got := table.type_to_str(field.typ)
// c.error('`$styp` incorrectly implements field `$ifield.name` of interface `$inter_sym.name`, expected `$exp`, got `$got`',
// pos)
return false
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
// c.error('`$styp` incorrectly implements interface `$inter_sym.name`, field `$ifield.name` must be mutable',
// pos)
return false
}
continue
}
// c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`",
// pos)
}
inter_sym.info.types << utyp
}
return true
}

View File

@ -6123,7 +6123,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
&& sym.kind == .interface_ {
// is interface
checked_type := c.unwrap_generic(left.typ)
should_skip = !c.table.type_implements_interface(checked_type,
should_skip = !c.table.does_type_implement_interface(checked_type,
got_type)
} else if left is ast.TypeNode {
is_comptime_type_is_expr = true

View File

@ -328,7 +328,7 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
checked_type := g.unwrap_generic(left.typ)
// TODO PERF this check is run twice (also in the checker)
// store the result in a field
is_true := g.table.type_implements_interface(checked_type,
is_true := g.table.does_type_implement_interface(checked_type,
got_type)
// true // exp_type in interface_sym.info.types
if cond.op == .key_is {