checker: force interface init (#10910)

pull/10999/head
Daniel Däschle 2021-07-30 02:18:20 +02:00 committed by GitHub
parent 1eac351f25
commit 6fa8e4269e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 87 additions and 19 deletions

View File

@ -57,6 +57,7 @@ fn new_cancel_context(parent Context) &CancelContext {
context: parent
mutex: sync.new_mutex()
done: chan int{cap: 2}
err: none
}
}

View File

@ -540,6 +540,10 @@ pub fn (mut c Checker) interface_decl(mut decl ast.InterfaceDecl) {
c.check_valid_snake_case(field.name, 'field name', field.pos)
}
c.ensure_type_exists(field.typ, field.pos) or { return }
if field.typ == decl.typ {
c.error('recursive interface fields are not allowed because they cannot be initialised',
field.type_pos)
}
for j in 0 .. i {
if field.name == decl.fields[j].name {
c.error('field name `$field.name` duplicate', field.pos)
@ -1154,6 +1158,12 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
c.error('reference field `${type_sym.name}.$field.name` must be initialized',
node.pos)
}
// Do not allow empty uninitialized interfaces
sym := c.table.get_type_symbol(field.typ)
if sym.kind == .interface_ {
c.error('interface field `${type_sym.name}.$field.name` must be initialized',
node.pos)
}
// Do not allow empty uninitialized sum types
/*
sym := c.table.get_type_symbol(field.typ)

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/interface_init_err.vv:15:7: error: interface field `Server.handler` must be initialized
13 |
14 | fn main() {
15 | _ := Server{}
| ~~~~~~~~
16 | }

View File

@ -0,0 +1,16 @@
interface Handler {
foo string
handle(int) int
}
struct Server {
handler Handler
}
fn (s Server) handle(x int) int {
return x
}
fn main() {
_ := Server{}
}

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/recursive_interface_err.vv:2:6: error: recursive interface fields are not allowed because they cannot be initialised
1 | interface Foo {
2 | foo Foo
| ~~~
3 | }

View File

@ -0,0 +1,3 @@
interface Foo {
foo Foo
}

View File

@ -1923,6 +1923,10 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
g.write('.msg))')
return
}
if got_sym.kind == .none_ && exp_sym.name == 'IError' {
g.expr(expr)
return
}
if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx()
&& !expected_type.has_flag(.optional) {
if expr is ast.StructInit && !got_type.is_ptr() {

View File

@ -5,7 +5,7 @@ import v.ast
pub struct Amd64 {
mut:
g &Gen
g Gen
// arm64 specific stuff for code generation
}

View File

@ -24,7 +24,7 @@ enum Arm64Register {
pub struct Arm64 {
mut:
g &Gen
g Gen
// arm64 specific stuff for code generation
}

View File

@ -15,7 +15,8 @@ import term
pub const builtins = ['println', 'exit']
interface CodeGen {
g &Gen
mut:
g Gen
gen_exit(mut g Gen, expr ast.Expr)
// XXX WHY gen_exit fn (expr ast.Expr)
}
@ -54,13 +55,13 @@ enum Size {
_64
}
fn (g &Gen) get_backend() ?CodeGen {
match g.pref.arch {
fn get_backend(arch pref.Arch) ?CodeGen {
match arch {
.arm64 {
return Arm64{g}
return Arm64{}
}
.amd64 {
return Amd64{g}
return Amd64{}
}
else {}
}
@ -73,11 +74,13 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
sect_header_name_pos: 0
out_name: out_name
pref: pref
// TODO: workaround, needs to support recursive init
cgen: get_backend(pref.arch) or {
eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.')
exit(1)
}
}
g.cgen = g.get_backend() or {
eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.')
exit(1)
}
g.cgen.g = g
g.generate_header()
for file in files {
if file.warnings.len > 0 {

View File

@ -9,6 +9,7 @@ fn test_macho() {
pref: &pref.Preferences{}
out_name: 'test.bin'
table: ast.new_table()
cgen: native.Amd64{}
}
g.generate_macho_header()
g.generate_macho_footer()

View File

@ -20,8 +20,7 @@ mut:
}
fn test_a_struct_implementing_an_interface_can_be_assigned_without_explicit_casts() {
mut anyplanet := AnyPlanet{}
anyplanet.planet = Moon{}
mut anyplanet := AnyPlanet{Moon{}}
assert anyplanet.planet.name() == 'moon'
anyplanet.planet = Mars{}
assert anyplanet.planet.name() == 'mars'

View File

@ -2,20 +2,30 @@ struct Base {
}
interface Foo {
parent Foo
parent Foo2
thing(mut b Base, value i64) string
}
interface Foo2 {
thing(mut b Base, value i64) string
}
struct Bar {
parent Foo
parent Foo2
}
struct Bar2 {}
fn (f Bar) thing(mut b Base, value i64) string {
return 'bar'
}
fn (f Bar2) thing(mut b Base, value i64) string {
return 'bar2'
}
struct SubBar {
parent Foo = Bar{}
parent Foo2 = Bar2{}
}
fn (f SubBar) thing(mut b Base, value i64) string {
@ -23,8 +33,8 @@ fn (f SubBar) thing(mut b Base, value i64) string {
}
fn test_interface_nested_field() {
mut foo_group := []Foo{}
foo_group << Bar{}
mut foo_group := []Foo2{}
foo_group << Bar2{}
foo_group << SubBar{}
mut b := Base{}
@ -34,7 +44,7 @@ fn test_interface_nested_field() {
ret << foo.thing(mut b, 22)
}
assert ret.len == 2
assert ret[0] == 'bar'
assert ret[0] == 'bar2'
assert ret[1] == 'subbar'
}

View File

@ -0,0 +1,10 @@
struct Test {
err IError
}
fn test_init_with_none() {
t := Test{
err: none
}
// compiles successfully
}