all: reimplement struct embedding with methods (#7506)
parent
7e8add24dd
commit
b27f5c378c
|
@ -113,6 +113,7 @@ pub mut:
|
|||
typ table.Type // type of the entire thing (`Foo.bar`)
|
||||
name_type table.Type // T in `T.name` or typeof in `typeof(expr).name`
|
||||
scope &Scope
|
||||
from_embed_type table.Type // holds the type of the embed that the method is called from
|
||||
}
|
||||
|
||||
// root_ident returns the origin ident where the selector started.
|
||||
|
@ -144,7 +145,6 @@ pub:
|
|||
has_default_expr bool
|
||||
attrs []table.Attr
|
||||
is_public bool
|
||||
is_embed bool
|
||||
pub mut:
|
||||
name string
|
||||
typ table.Type
|
||||
|
@ -192,10 +192,17 @@ pub:
|
|||
is_union bool
|
||||
attrs []table.Attr
|
||||
end_comments []Comment
|
||||
embeds []Embed
|
||||
pub mut:
|
||||
fields []StructField
|
||||
}
|
||||
|
||||
pub struct Embed {
|
||||
pub:
|
||||
typ table.Type
|
||||
pos token.Position
|
||||
}
|
||||
|
||||
pub struct StructEmbedding {
|
||||
pub:
|
||||
name string
|
||||
|
@ -225,6 +232,18 @@ pub mut:
|
|||
expected_type table.Type
|
||||
}
|
||||
|
||||
pub struct StructInitEmbed {
|
||||
pub:
|
||||
expr Expr
|
||||
pos token.Position
|
||||
comments []Comment
|
||||
next_comments []Comment
|
||||
pub mut:
|
||||
name string
|
||||
typ table.Type
|
||||
expected_type table.Type
|
||||
}
|
||||
|
||||
pub struct StructInit {
|
||||
pub:
|
||||
pos token.Position
|
||||
|
@ -233,6 +252,7 @@ pub:
|
|||
pub mut:
|
||||
typ table.Type
|
||||
fields []StructInitField
|
||||
embeds []StructInitEmbed
|
||||
}
|
||||
|
||||
// import statement
|
||||
|
@ -320,6 +340,7 @@ pub mut:
|
|||
generic_list_pos token.Position
|
||||
free_receiver bool // true if the receiver expression needs to be freed
|
||||
scope &Scope
|
||||
from_embed_type table.Type // holds the type of the embed that the method is called from
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -371,27 +371,17 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
|
|||
}
|
||||
mut struct_sym := c.table.find_type(decl.name) or { table.TypeSymbol{} }
|
||||
if mut struct_sym.info is table.Struct {
|
||||
for embed in decl.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed.typ)
|
||||
if embed_sym.kind != .struct_ {
|
||||
c.error('`$embed_sym.name` is not a struct', embed.pos)
|
||||
}
|
||||
}
|
||||
for i, field in decl.fields {
|
||||
if decl.language == .v && !field.is_embed {
|
||||
if decl.language == .v {
|
||||
c.check_valid_snake_case(field.name, 'field name', field.pos)
|
||||
}
|
||||
sym := c.table.get_type_symbol(field.typ)
|
||||
if field.is_embed {
|
||||
if mut sym.info is table.Struct {
|
||||
for embed_field in sym.info.fields {
|
||||
already_exists := struct_sym.info.fields.filter(it.name == embed_field.name).len >
|
||||
0
|
||||
if !already_exists {
|
||||
struct_sym.info.fields << {
|
||||
embed_field |
|
||||
embed_alias_for: field.name
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.error('`$sym.name` is not a struct', field.pos)
|
||||
}
|
||||
}
|
||||
for j in 0 .. i {
|
||||
if field.name == decl.fields[j].name {
|
||||
c.error('field name `$field.name` duplicate', field.pos)
|
||||
|
@ -528,6 +518,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
mut inited_fields := []string{}
|
||||
for i, field in struct_init.fields {
|
||||
mut info_field := table.Field{}
|
||||
mut embed_type := table.Type(0)
|
||||
mut is_embed := false
|
||||
mut field_name := ''
|
||||
if struct_init.is_short {
|
||||
if i >= info.fields.len {
|
||||
|
@ -548,31 +540,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
break
|
||||
}
|
||||
}
|
||||
/*
|
||||
if c.pref.is_verbose {
|
||||
for f in info.fields {
|
||||
if f.name == field_name {
|
||||
if f.embed_alias_for.len != 0 {
|
||||
mut has_embed_init := false
|
||||
for embedding in struct_init.fields {
|
||||
if embedding.name == f.embed_alias_for {
|
||||
has_embed_init = true
|
||||
}
|
||||
}
|
||||
if !has_embed_init {
|
||||
n := {
|
||||
f |
|
||||
embed_alias_for: ''
|
||||
}
|
||||
println(field)
|
||||
// struct_init.fields << { f | embed_alias_for: '' }
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
for embed in info.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed)
|
||||
if embed_sym.embed_name() == field_name {
|
||||
exists = true
|
||||
embed_type = embed
|
||||
is_embed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if !exists {
|
||||
c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`',
|
||||
field.pos)
|
||||
|
@ -584,6 +562,19 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
continue
|
||||
}
|
||||
}
|
||||
if is_embed {
|
||||
c.expected_type = embed_type
|
||||
expr_type := c.expr(field.expr)
|
||||
expr_type_sym := c.table.get_type_symbol(expr_type)
|
||||
if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
|
||||
c.check_expected(expr_type, embed_type) or {
|
||||
c.error('cannot assign to field `$info_field.name`: $err',
|
||||
field.pos)
|
||||
}
|
||||
}
|
||||
struct_init.fields[i].typ = expr_type
|
||||
struct_init.fields[i].expected_type = embed_type
|
||||
} else {
|
||||
inited_fields << field_name
|
||||
field_type_sym := c.table.get_type_symbol(info_field.typ)
|
||||
c.expected_type = info_field.typ
|
||||
|
@ -593,7 +584,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
c.type_implements(expr_type, info_field.typ, field.pos)
|
||||
} else if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
|
||||
c.check_expected(expr_type, info_field.typ) or {
|
||||
c.error('cannot assign to field `$info_field.name`: $err', field.pos)
|
||||
c.error('cannot assign to field `$info_field.name`: $err',
|
||||
field.pos)
|
||||
}
|
||||
}
|
||||
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() &&
|
||||
|
@ -603,10 +595,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
struct_init.fields[i].typ = expr_type
|
||||
struct_init.fields[i].expected_type = info_field.typ
|
||||
}
|
||||
}
|
||||
// Check uninitialized refs
|
||||
for field in info.fields {
|
||||
if field.has_default_expr || field.name in inited_fields || field.embed_alias_for !=
|
||||
'' {
|
||||
if field.has_default_expr || field.name in inited_fields {
|
||||
continue
|
||||
}
|
||||
if field.typ.is_ptr() && !c.pref.translated {
|
||||
|
@ -992,7 +984,23 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
|||
match typ_sym.kind {
|
||||
.struct_ {
|
||||
struct_info := typ_sym.info as table.Struct
|
||||
field_info := struct_info.find_field(expr.field_name) or {
|
||||
mut has_field := true
|
||||
mut field_info := struct_info.find_field(expr.field_name) or {
|
||||
has_field = false
|
||||
table.Field{}
|
||||
}
|
||||
if !has_field {
|
||||
for embed in struct_info.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed)
|
||||
embed_struct_info := embed_sym.info as table.Struct
|
||||
if embed_field_info := embed_struct_info.find_field(expr.field_name) {
|
||||
has_field = true
|
||||
field_info = embed_field_info
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !has_field {
|
||||
type_str := c.table.type_to_str(expr.expr_type)
|
||||
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
||||
return '', pos
|
||||
|
@ -1238,7 +1246,36 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position())
|
||||
}
|
||||
}
|
||||
if method := c.table.type_find_method(left_type_sym, method_name) {
|
||||
mut method := table.Fn{}
|
||||
mut has_method := false
|
||||
if m := c.table.type_find_method(left_type_sym, method_name) {
|
||||
method = m
|
||||
has_method = true
|
||||
} else {
|
||||
if left_type_sym.info is table.Struct {
|
||||
mut found_methods := []table.Fn{}
|
||||
mut embed_of_found_methods := []table.Type{}
|
||||
for embed in left_type_sym.info.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed)
|
||||
if m := c.table.type_find_method(embed_sym, method_name) {
|
||||
found_methods << m
|
||||
embed_of_found_methods << embed
|
||||
}
|
||||
}
|
||||
if found_methods.len == 1 {
|
||||
method = found_methods[0]
|
||||
has_method = true
|
||||
call_expr.from_embed_type = embed_of_found_methods[0]
|
||||
} else if found_methods.len > 1 {
|
||||
c.error('ambiguous method `$method_name`', call_expr.pos)
|
||||
}
|
||||
}
|
||||
if left_type_sym.kind == .aggregate {
|
||||
// the error message contains the problematic type
|
||||
unknown_method_msg = err
|
||||
}
|
||||
}
|
||||
if has_method {
|
||||
if !method.is_pub && !c.is_builtin_mod && !c.pref.is_test && left_type_sym.mod != c.mod &&
|
||||
left_type_sym.mod != '' { // method.mod != c.mod {
|
||||
// If a private method is called outside of the module
|
||||
|
@ -1337,15 +1374,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
// TODO: cant we just set all these to the concrete type in checker? then no need in gen
|
||||
call_expr.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic)
|
||||
} else {
|
||||
// note: correct receiver type is automatically set here on struct embed calls
|
||||
call_expr.receiver_type = method.params[0].typ
|
||||
}
|
||||
call_expr.return_type = method.return_type
|
||||
return method.return_type
|
||||
} else {
|
||||
if left_type_sym.kind == .aggregate {
|
||||
// the error message contains the problematic type
|
||||
unknown_method_msg = err
|
||||
}
|
||||
}
|
||||
// TODO: str methods
|
||||
if method_name == 'str' {
|
||||
|
@ -1846,7 +1879,47 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
|
|||
}
|
||||
}
|
||||
mut unknown_field_msg := 'type `$sym.name` has no field or method `$field_name`'
|
||||
if field := c.table.struct_find_field(sym, field_name) {
|
||||
mut has_field := false
|
||||
mut field := table.Field{}
|
||||
if field_name.len > 0 && field_name[0].is_capital() && sym.info is table.Struct {
|
||||
// x.Foo.y => access the embedded struct
|
||||
sym_info := sym.info as table.Struct
|
||||
for embed in sym_info.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed)
|
||||
if embed_sym.embed_name() == field_name {
|
||||
selector_expr.typ = embed
|
||||
return embed
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if f := c.table.struct_find_field(sym, field_name) {
|
||||
has_field = true
|
||||
field = f
|
||||
} else {
|
||||
if sym.info is table.Struct {
|
||||
mut found_fields := []table.Field{}
|
||||
mut embed_of_found_fields := []table.Type{}
|
||||
for embed in sym.info.embeds {
|
||||
embed_sym := c.table.get_type_symbol(embed)
|
||||
if f := c.table.struct_find_field(embed_sym, field_name) {
|
||||
found_fields << f
|
||||
embed_of_found_fields << embed
|
||||
}
|
||||
}
|
||||
if found_fields.len == 1 {
|
||||
field = found_fields[0]
|
||||
has_field = true
|
||||
selector_expr.from_embed_type = embed_of_found_fields[0]
|
||||
} else if found_fields.len > 1 {
|
||||
c.error('ambiguous field `$field_name`', selector_expr.pos)
|
||||
}
|
||||
}
|
||||
if sym.kind == .aggregate {
|
||||
unknown_field_msg = err
|
||||
}
|
||||
}
|
||||
}
|
||||
if has_field {
|
||||
if sym.mod != c.mod && !field.is_pub && sym.language != .c {
|
||||
c.error('field `${sym.name}.$field_name` is not public', selector_expr.pos)
|
||||
}
|
||||
|
@ -1860,19 +1933,14 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
|
|||
}
|
||||
selector_expr.typ = field.typ
|
||||
return field.typ
|
||||
} else {
|
||||
if sym.kind == .aggregate {
|
||||
unknown_field_msg = err
|
||||
}
|
||||
}
|
||||
if sym.kind !in [.struct_, .aggregate] {
|
||||
if sym.kind != .placeholder {
|
||||
c.error('`$sym.name` is not a struct', selector_expr.pos)
|
||||
}
|
||||
} else {
|
||||
if sym.kind == .struct_ {
|
||||
sss := sym.info as table.Struct
|
||||
suggestion := util.new_suggestion(field_name, sss.fields.map(it.name))
|
||||
if sym.info is table.Struct {
|
||||
suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name))
|
||||
c.error(suggestion.say(unknown_field_msg), selector_expr.pos)
|
||||
}
|
||||
c.error(unknown_field_msg, selector_expr.pos)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
vlib/v/checker/tests/ambiguous_field_method_err.vv:22:4: error: ambiguous method `test`
|
||||
20 | fn main() {
|
||||
21 | b := Bar{}
|
||||
22 | b.test()
|
||||
| ~~~~~~
|
||||
23 | n := b.name
|
||||
24 | }
|
||||
vlib/v/checker/tests/ambiguous_field_method_err.vv:23:9: error: ambiguous field `name`
|
||||
21 | b := Bar{}
|
||||
22 | b.test()
|
||||
23 | n := b.name
|
||||
| ~~~~
|
||||
24 | }
|
|
@ -0,0 +1,24 @@
|
|||
struct Foo {
|
||||
name int = 5
|
||||
}
|
||||
struct Bar {
|
||||
Foo
|
||||
Foo2
|
||||
}
|
||||
|
||||
struct Foo2 {
|
||||
name string
|
||||
}
|
||||
fn (f Foo2) test() {
|
||||
println(f)
|
||||
}
|
||||
|
||||
fn (f Foo) test() {
|
||||
println(f)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
b := Bar{}
|
||||
b.test()
|
||||
n := b.name
|
||||
}
|
|
@ -630,13 +630,11 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl) {
|
|||
max_type = ft.len
|
||||
}
|
||||
}
|
||||
for field in node.fields.filter(it.is_embed) {
|
||||
f.writeln('\t$field.name')
|
||||
for embed in node.embeds {
|
||||
styp := f.table.type_to_str(embed.typ)
|
||||
f.writeln('\t$styp')
|
||||
}
|
||||
for i, field in node.fields {
|
||||
if field.is_embed {
|
||||
continue
|
||||
}
|
||||
if i == node.mut_pos {
|
||||
f.writeln('mut:')
|
||||
} else if i == node.pub_pos {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
struct Foo {
|
||||
x int
|
||||
}
|
||||
|
||||
struct Test {}
|
||||
|
||||
struct Bar {
|
||||
y int
|
||||
Foo
|
||||
z string
|
||||
Test
|
||||
}
|
|
@ -2664,6 +2664,70 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.struct_init(node)
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
g.selector_expr(node)
|
||||
}
|
||||
ast.Type {
|
||||
// match sum Type
|
||||
// g.write('/* Type */')
|
||||
// type_idx := node.typ.idx()
|
||||
sym := g.table.get_type_symbol(node.typ)
|
||||
sidx := g.type_sidx(node.typ)
|
||||
// g.write('$type_idx /* $sym.name */')
|
||||
g.write('$sidx /* $sym.name */')
|
||||
}
|
||||
ast.TypeOf {
|
||||
g.typeof_expr(node)
|
||||
}
|
||||
ast.Likely {
|
||||
if node.is_likely {
|
||||
g.write('_likely_')
|
||||
} else {
|
||||
g.write('_unlikely_')
|
||||
}
|
||||
g.write('(')
|
||||
g.expr(node.expr)
|
||||
g.write(')')
|
||||
}
|
||||
ast.UnsafeExpr {
|
||||
g.expr(node.expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// T.name, typeof(expr).name
|
||||
fn (mut g Gen) type_name(type_ table.Type) {
|
||||
mut typ := type_
|
||||
if typ.has_flag(.generic) {
|
||||
typ = g.cur_generic_type
|
||||
}
|
||||
s := g.table.type_to_str(typ)
|
||||
g.write('_SLIT("${util.strip_main_name(s)}")')
|
||||
}
|
||||
|
||||
fn (mut g Gen) typeof_expr(node ast.TypeOf) {
|
||||
sym := g.table.get_type_symbol(node.expr_type)
|
||||
if sym.kind == .sum_type {
|
||||
// When encountering a .sum_type, typeof() should be done at runtime,
|
||||
// because the subtype of the expression may change:
|
||||
sum_type_idx := node.expr_type.idx()
|
||||
g.write('tos3( /* $sym.name */ v_typeof_sumtype_${sum_type_idx}( (')
|
||||
g.expr(node.expr)
|
||||
g.write(').typ ))')
|
||||
} else if sym.kind == .array_fixed {
|
||||
fixed_info := sym.info as table.ArrayFixed
|
||||
typ_name := g.table.get_type_name(fixed_info.elem_type)
|
||||
g.write('_SLIT("[$fixed_info.size]${util.strip_main_name(typ_name)}")')
|
||||
} else if sym.kind == .function {
|
||||
info := sym.info as table.FnType
|
||||
g.write('_SLIT("${g.fn_decl_str(info)}")')
|
||||
} else if node.expr_type.has_flag(.variadic) {
|
||||
g.write('_SLIT("...${util.strip_main_name(sym.name)}")')
|
||||
} else {
|
||||
g.write('_SLIT("${util.strip_main_name(sym.name)}")')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
|
||||
g.prevent_sum_type_unwrapping_once = false
|
||||
if node.name_type > 0 {
|
||||
|
@ -2725,14 +2789,11 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.write('.data)')
|
||||
}
|
||||
// struct embedding
|
||||
if sym.kind == .struct_ {
|
||||
sym_info := sym.info as table.Struct
|
||||
x := sym_info.fields.filter(it.name == node.field_name)
|
||||
if x.len > 0 {
|
||||
field := x[0]
|
||||
if field.embed_alias_for != '' {
|
||||
g.write('.$field.embed_alias_for')
|
||||
}
|
||||
if sym.info is table.Struct {
|
||||
if node.from_embed_type != 0 {
|
||||
embed_sym := g.table.get_type_symbol(node.from_embed_type)
|
||||
embed_name := embed_sym.embed_name()
|
||||
g.write('.$embed_name')
|
||||
}
|
||||
}
|
||||
if node.expr_type.is_ptr() || sym.kind == .chan {
|
||||
|
@ -2752,66 +2813,6 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.write('.$sum_type_deref_field)')
|
||||
}
|
||||
}
|
||||
ast.Type {
|
||||
// match sum Type
|
||||
// g.write('/* Type */')
|
||||
// type_idx := node.typ.idx()
|
||||
sym := g.table.get_type_symbol(node.typ)
|
||||
sidx := g.type_sidx(node.typ)
|
||||
// g.write('$type_idx /* $sym.name */')
|
||||
g.write('$sidx /* $sym.name */')
|
||||
}
|
||||
ast.TypeOf {
|
||||
g.typeof_expr(node)
|
||||
}
|
||||
ast.Likely {
|
||||
if node.is_likely {
|
||||
g.write('_likely_')
|
||||
} else {
|
||||
g.write('_unlikely_')
|
||||
}
|
||||
g.write('(')
|
||||
g.expr(node.expr)
|
||||
g.write(')')
|
||||
}
|
||||
ast.UnsafeExpr {
|
||||
g.expr(node.expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// T.name, typeof(expr).name
|
||||
fn (mut g Gen) type_name(type_ table.Type) {
|
||||
mut typ := type_
|
||||
if typ.has_flag(.generic) {
|
||||
typ = g.cur_generic_type
|
||||
}
|
||||
s := g.table.type_to_str(typ)
|
||||
g.write('_SLIT("${util.strip_main_name(s)}")')
|
||||
}
|
||||
|
||||
fn (mut g Gen) typeof_expr(node ast.TypeOf) {
|
||||
sym := g.table.get_type_symbol(node.expr_type)
|
||||
if sym.kind == .sum_type {
|
||||
// When encountering a .sum_type, typeof() should be done at runtime,
|
||||
// because the subtype of the expression may change:
|
||||
sum_type_idx := node.expr_type.idx()
|
||||
g.write('tos3( /* $sym.name */ v_typeof_sumtype_${sum_type_idx}( (')
|
||||
g.expr(node.expr)
|
||||
g.write(').typ ))')
|
||||
} else if sym.kind == .array_fixed {
|
||||
fixed_info := sym.info as table.ArrayFixed
|
||||
typ_name := g.table.get_type_name(fixed_info.elem_type)
|
||||
g.write('_SLIT("[$fixed_info.size]${util.strip_main_name(typ_name)}")')
|
||||
} else if sym.kind == .function {
|
||||
info := sym.info as table.FnType
|
||||
g.write('_SLIT("${g.fn_decl_str(info)}")')
|
||||
} else if node.expr_type.has_flag(.variadic) {
|
||||
g.write('_SLIT("...${util.strip_main_name(sym.name)}")')
|
||||
} else {
|
||||
g.write('_SLIT("${util.strip_main_name(sym.name)}")')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) enum_expr(node ast.Expr) {
|
||||
match node {
|
||||
|
@ -4347,16 +4348,6 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
mut initialized := false
|
||||
for i, field in struct_init.fields {
|
||||
inited_fields[field.name] = i
|
||||
if mut sym.info is table.Struct {
|
||||
equal_fields := sym.info.fields.filter(it.name == field.name)
|
||||
if equal_fields.len == 0 {
|
||||
continue
|
||||
}
|
||||
tfield := equal_fields[0]
|
||||
if tfield.embed_alias_for.len != 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if sym.kind != .struct_ {
|
||||
field_name := c_name(field.name)
|
||||
g.write('.$field_name = ')
|
||||
|
@ -4396,6 +4387,23 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
if info.is_union && struct_init.fields.len > 1 {
|
||||
verror('union must not have more than 1 initializer')
|
||||
}
|
||||
for embed in info.embeds {
|
||||
embed_sym := g.table.get_type_symbol(embed)
|
||||
embed_name := embed_sym.embed_name()
|
||||
if embed_name !in inited_fields {
|
||||
default_init := ast.StructInit{
|
||||
typ: embed
|
||||
}
|
||||
g.write('.$embed_name = ')
|
||||
g.struct_init(default_init)
|
||||
if is_multiline {
|
||||
g.writeln(',')
|
||||
} else {
|
||||
g.write(',')
|
||||
}
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
// g.zero_struct_fields(info, inited_fields)
|
||||
// nr_fields = info.fields.len
|
||||
for field in info.fields {
|
||||
|
@ -4404,10 +4412,6 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
if equal_fields.len == 0 {
|
||||
continue
|
||||
}
|
||||
tfield := equal_fields[0]
|
||||
if tfield.embed_alias_for.len != 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if field.name in inited_fields {
|
||||
sfield := struct_init.fields[inited_fields[field.name]]
|
||||
|
@ -4792,8 +4796,8 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
|||
} else {
|
||||
g.type_definitions.writeln('struct $name {')
|
||||
}
|
||||
if typ.info.fields.len > 0 {
|
||||
for field in typ.info.fields.filter(it.embed_alias_for == '') {
|
||||
if typ.info.fields.len > 0 || typ.info.embeds.len > 0 {
|
||||
for field in typ.info.fields {
|
||||
// Some of these structs may want to contain
|
||||
// optionals that may not be defined at this point
|
||||
// if this is the case then we are going to
|
||||
|
@ -4887,6 +4891,14 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol {
|
|||
// if info.is_interface {
|
||||
// continue
|
||||
// }
|
||||
for embed in t.info.embeds {
|
||||
dep := g.table.get_type_symbol(embed).name
|
||||
// skip if not in types list or already in deps
|
||||
if dep !in type_names || dep in field_deps {
|
||||
continue
|
||||
}
|
||||
field_deps << dep
|
||||
}
|
||||
for field in t.info.fields {
|
||||
dep := g.table.get_type_symbol(field.typ).name
|
||||
// skip if not in types list or already in deps
|
||||
|
|
|
@ -457,6 +457,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
g.write('/*af receiver arg*/' + arg_name)
|
||||
} else {
|
||||
g.expr(node.left)
|
||||
if node.from_embed_type != 0 {
|
||||
embed_name := typ_sym.embed_name()
|
||||
g.write('.$embed_name')
|
||||
}
|
||||
}
|
||||
if has_cast {
|
||||
g.write(')')
|
||||
|
|
|
@ -77,7 +77,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
// println('struct decl $name')
|
||||
mut ast_fields := []ast.StructField{}
|
||||
mut fields := []table.Field{}
|
||||
mut embedded_structs := []table.Type{}
|
||||
mut embed_types := []table.Type{}
|
||||
mut embeds := []ast.Embed{}
|
||||
mut embed_field_names := []string{}
|
||||
mut mut_pos := -1
|
||||
mut pub_pos := -1
|
||||
mut pub_mut_pos := -1
|
||||
|
@ -156,35 +158,38 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
field_start_pos := p.tok.position()
|
||||
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) ||
|
||||
p.peek_tok.kind == .dot) &&
|
||||
language == .v
|
||||
language == .v && ast_fields.len == 0
|
||||
mut field_name := ''
|
||||
mut typ := table.Type(0)
|
||||
mut type_pos := token.Position{}
|
||||
mut field_pos := token.Position{}
|
||||
if is_embed {
|
||||
// struct embedding
|
||||
type_pos = p.tok.position()
|
||||
typ = p.parse_type()
|
||||
sym := p.table.get_type_symbol(typ)
|
||||
// main.Abc<int> => Abc
|
||||
mut symbol_name := sym.name.split('.')[1]
|
||||
// remove generic part from name
|
||||
if '<' in symbol_name {
|
||||
symbol_name = symbol_name.split('<')[0]
|
||||
}
|
||||
for p.tok.kind == .comment {
|
||||
comments << p.comment()
|
||||
if p.tok.kind == .rcbr {
|
||||
break
|
||||
}
|
||||
}
|
||||
type_pos = p.prev_tok.position()
|
||||
field_pos = p.prev_tok.position()
|
||||
field_name = symbol_name
|
||||
if typ in embedded_structs {
|
||||
p.error_with_pos('cannot embed `$field_name` more than once', type_pos)
|
||||
type_pos = type_pos.extend(p.prev_tok.position())
|
||||
sym := p.table.get_type_symbol(typ)
|
||||
if typ in embed_types {
|
||||
p.error_with_pos('cannot embed `$sym.name` more than once', type_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
embedded_structs << typ
|
||||
field_name = sym.embed_name()
|
||||
if field_name in embed_field_names {
|
||||
p.error_with_pos('duplicate field `$field_name`', type_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
embed_field_names << field_name
|
||||
embed_types << typ
|
||||
embeds << ast.Embed{
|
||||
typ: typ
|
||||
pos: type_pos
|
||||
}
|
||||
} else {
|
||||
// struct field
|
||||
field_name = p.check_name()
|
||||
|
@ -225,7 +230,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
}
|
||||
has_default_expr = true
|
||||
}
|
||||
}
|
||||
// TODO merge table and ast Fields?
|
||||
ast_fields << ast.StructField{
|
||||
name: field_name
|
||||
|
@ -237,8 +241,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
has_default_expr: has_default_expr
|
||||
attrs: p.attrs
|
||||
is_public: is_field_pub
|
||||
is_embed: is_embed
|
||||
}
|
||||
}
|
||||
// save embeds as table fields too, it will be used in generation phase
|
||||
fields << table.Field{
|
||||
name: field_name
|
||||
typ: typ
|
||||
|
@ -248,7 +253,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
is_mut: is_field_mut
|
||||
is_global: is_field_global
|
||||
attrs: p.attrs
|
||||
is_embed: is_embed
|
||||
}
|
||||
p.attrs = []
|
||||
}
|
||||
|
@ -270,6 +274,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
cname: util.no_dots(name)
|
||||
mod: p.mod
|
||||
info: table.Struct{
|
||||
embeds: embed_types
|
||||
fields: fields
|
||||
is_typedef: attrs.contains('typedef')
|
||||
is_union: is_union
|
||||
|
@ -301,6 +306,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
attrs: attrs
|
||||
end_comments: end_comments
|
||||
gen_types: generic_types
|
||||
embeds: embeds
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/parser/tests/duplicate_field_embed_err.vv:8:2: error: duplicate field `ModFileAndFolder`
|
||||
6 | struct Bar {
|
||||
7 | ModFileAndFolder
|
||||
8 | vmod.ModFileAndFolder
|
||||
| ~~~~~~~~~~~~~~~~~~~~~
|
||||
9 | }
|
|
@ -0,0 +1,9 @@
|
|||
import v.vmod
|
||||
|
||||
struct ModFileAndFolder {
|
||||
name int = 5
|
||||
}
|
||||
struct Bar {
|
||||
ModFileAndFolder
|
||||
vmod.ModFileAndFolder
|
||||
}
|
|
@ -606,6 +606,7 @@ pub fn (kinds []Kind) str() string {
|
|||
|
||||
pub struct Struct {
|
||||
pub mut:
|
||||
embeds []Type
|
||||
fields []Field
|
||||
is_typedef bool // C. [typedef]
|
||||
is_union bool
|
||||
|
@ -663,8 +664,6 @@ pub mut:
|
|||
is_pub bool
|
||||
is_mut bool
|
||||
is_global bool
|
||||
is_embed bool
|
||||
embed_alias_for string // name of the struct which contains this field name
|
||||
}
|
||||
|
||||
fn (f &Field) equals(o &Field) bool {
|
||||
|
@ -850,6 +849,17 @@ pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string {
|
|||
return sb.str()
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) embed_name() string {
|
||||
// main.Abc<int> => Abc<int>
|
||||
mut embed_name := t.name.split('.').last()
|
||||
// remove generic part from name
|
||||
// Abc<int> => Abc
|
||||
if '<' in embed_name {
|
||||
embed_name = embed_name.split('<')[0]
|
||||
}
|
||||
return embed_name
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) has_method(name string) bool {
|
||||
t.find_method(name) or { return false }
|
||||
return true
|
||||
|
|
|
@ -7,7 +7,6 @@ struct Foo {
|
|||
|
||||
fn (f Foo) foo() {}
|
||||
|
||||
|
||||
struct Bar {
|
||||
Foo
|
||||
}
|
||||
|
@ -15,7 +14,7 @@ struct Bar {
|
|||
fn test_embed() {
|
||||
b := Bar{}
|
||||
assert b.x == 0
|
||||
//b.foo() // TODO methods
|
||||
b.foo()
|
||||
}
|
||||
|
||||
fn test_embed_direct_access() {
|
||||
|
@ -27,7 +26,7 @@ fn test_default_value() {
|
|||
b := Bar{Foo: Foo{}}
|
||||
assert b.y == 5
|
||||
}
|
||||
/*
|
||||
/* TODO
|
||||
fn test_initialize() {
|
||||
b := Bar{x: 1, y: 2}
|
||||
assert b.x == 1
|
||||
|
@ -59,5 +58,19 @@ fn test_generic_embed() {
|
|||
b := BarGenericContainer{}
|
||||
assert b.BarGeneric.foo == 0
|
||||
assert b.foo == 0
|
||||
println('ok')
|
||||
}
|
||||
|
||||
struct Upper {
|
||||
mut:
|
||||
x int
|
||||
}
|
||||
|
||||
struct UpperHolder {
|
||||
Upper
|
||||
}
|
||||
|
||||
fn test_assign() {
|
||||
mut h := UpperHolder{}
|
||||
h.x = 5
|
||||
assert h.x == 5
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue