checker: force interface init (#10910)
parent
1eac351f25
commit
6fa8e4269e
|
@ -57,6 +57,7 @@ fn new_cancel_context(parent Context) &CancelContext {
|
|||
context: parent
|
||||
mutex: sync.new_mutex()
|
||||
done: chan int{cap: 2}
|
||||
err: none
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 | }
|
|
@ -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{}
|
||||
}
|
|
@ -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 | }
|
|
@ -0,0 +1,3 @@
|
|||
interface Foo {
|
||||
foo Foo
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import v.ast
|
|||
|
||||
pub struct Amd64 {
|
||||
mut:
|
||||
g &Gen
|
||||
g Gen
|
||||
// arm64 specific stuff for code generation
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ enum Arm64Register {
|
|||
|
||||
pub struct Arm64 {
|
||||
mut:
|
||||
g &Gen
|
||||
g Gen
|
||||
// arm64 specific stuff for code generation
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
g.cgen = g.get_backend() or {
|
||||
// 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 = g
|
||||
g.generate_header()
|
||||
for file in files {
|
||||
if file.warnings.len > 0 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
struct Test {
|
||||
err IError
|
||||
}
|
||||
|
||||
fn test_init_with_none() {
|
||||
t := Test{
|
||||
err: none
|
||||
}
|
||||
// compiles successfully
|
||||
}
|
Loading…
Reference in New Issue