all: reimplement struct embedding with methods (#7506)
parent
7e8add24dd
commit
b27f5c378c
|
@ -1209,7 +1209,7 @@ mut:
|
||||||
|
|
||||||
struct Button {
|
struct Button {
|
||||||
Widget
|
Widget
|
||||||
title string
|
title string
|
||||||
}
|
}
|
||||||
|
|
||||||
mut button := Button{
|
mut button := Button{
|
||||||
|
|
|
@ -103,16 +103,17 @@ pub:
|
||||||
// `foo.bar`
|
// `foo.bar`
|
||||||
pub struct SelectorExpr {
|
pub struct SelectorExpr {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
expr Expr // expr.field_name
|
expr Expr // expr.field_name
|
||||||
field_name string
|
field_name string
|
||||||
is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable
|
is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable
|
||||||
mut_pos token.Position
|
mut_pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
expr_type table.Type // type of `Foo` in `Foo.bar`
|
expr_type table.Type // type of `Foo` in `Foo.bar`
|
||||||
typ table.Type // type of the entire thing (`Foo.bar`)
|
typ table.Type // type of the entire thing (`Foo.bar`)
|
||||||
name_type table.Type // T in `T.name` or typeof in `typeof(expr).name`
|
name_type table.Type // T in `T.name` or typeof in `typeof(expr).name`
|
||||||
scope &Scope
|
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.
|
// root_ident returns the origin ident where the selector started.
|
||||||
|
@ -144,7 +145,6 @@ pub:
|
||||||
has_default_expr bool
|
has_default_expr bool
|
||||||
attrs []table.Attr
|
attrs []table.Attr
|
||||||
is_public bool
|
is_public bool
|
||||||
is_embed bool
|
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
typ table.Type
|
typ table.Type
|
||||||
|
@ -192,10 +192,17 @@ pub:
|
||||||
is_union bool
|
is_union bool
|
||||||
attrs []table.Attr
|
attrs []table.Attr
|
||||||
end_comments []Comment
|
end_comments []Comment
|
||||||
|
embeds []Embed
|
||||||
pub mut:
|
pub mut:
|
||||||
fields []StructField
|
fields []StructField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Embed {
|
||||||
|
pub:
|
||||||
|
typ table.Type
|
||||||
|
pos token.Position
|
||||||
|
}
|
||||||
|
|
||||||
pub struct StructEmbedding {
|
pub struct StructEmbedding {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
|
@ -225,6 +232,18 @@ pub mut:
|
||||||
expected_type table.Type
|
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 struct StructInit {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
@ -233,6 +252,7 @@ pub:
|
||||||
pub mut:
|
pub mut:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
fields []StructInitField
|
fields []StructInitField
|
||||||
|
embeds []StructInitEmbed
|
||||||
}
|
}
|
||||||
|
|
||||||
// import statement
|
// import statement
|
||||||
|
@ -320,6 +340,7 @@ pub mut:
|
||||||
generic_list_pos token.Position
|
generic_list_pos token.Position
|
||||||
free_receiver bool // true if the receiver expression needs to be freed
|
free_receiver bool // true if the receiver expression needs to be freed
|
||||||
scope &Scope
|
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{} }
|
mut struct_sym := c.table.find_type(decl.name) or { table.TypeSymbol{} }
|
||||||
if mut struct_sym.info is table.Struct {
|
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 {
|
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)
|
c.check_valid_snake_case(field.name, 'field name', field.pos)
|
||||||
}
|
}
|
||||||
sym := c.table.get_type_symbol(field.typ)
|
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 {
|
for j in 0 .. i {
|
||||||
if field.name == decl.fields[j].name {
|
if field.name == decl.fields[j].name {
|
||||||
c.error('field name `$field.name` duplicate', field.pos)
|
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{}
|
mut inited_fields := []string{}
|
||||||
for i, field in struct_init.fields {
|
for i, field in struct_init.fields {
|
||||||
mut info_field := table.Field{}
|
mut info_field := table.Field{}
|
||||||
|
mut embed_type := table.Type(0)
|
||||||
|
mut is_embed := false
|
||||||
mut field_name := ''
|
mut field_name := ''
|
||||||
if struct_init.is_short {
|
if struct_init.is_short {
|
||||||
if i >= info.fields.len {
|
if i >= info.fields.len {
|
||||||
|
@ -548,31 +540,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
if !exists {
|
||||||
if c.pref.is_verbose {
|
for embed in info.embeds {
|
||||||
for f in info.fields {
|
embed_sym := c.table.get_type_symbol(embed)
|
||||||
if f.name == field_name {
|
if embed_sym.embed_name() == field_name {
|
||||||
if f.embed_alias_for.len != 0 {
|
exists = true
|
||||||
mut has_embed_init := false
|
embed_type = embed
|
||||||
for embedding in struct_init.fields {
|
is_embed = true
|
||||||
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: '' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
if !exists {
|
if !exists {
|
||||||
c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`',
|
c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`',
|
||||||
field.pos)
|
field.pos)
|
||||||
|
@ -584,29 +562,43 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inited_fields << field_name
|
if is_embed {
|
||||||
field_type_sym := c.table.get_type_symbol(info_field.typ)
|
c.expected_type = embed_type
|
||||||
c.expected_type = info_field.typ
|
expr_type := c.expr(field.expr)
|
||||||
expr_type := c.expr(field.expr)
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
||||||
expr_type_sym := c.table.get_type_symbol(expr_type)
|
if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
|
||||||
if field_type_sym.kind == .interface_ {
|
c.check_expected(expr_type, embed_type) or {
|
||||||
c.type_implements(expr_type, info_field.typ, field.pos)
|
c.error('cannot assign to field `$info_field.name`: $err',
|
||||||
} else if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
|
field.pos)
|
||||||
c.check_expected(expr_type, info_field.typ) 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
|
||||||
|
expr_type := c.expr(field.expr)
|
||||||
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
||||||
|
if field_type_sym.kind == .interface_ {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() &&
|
||||||
|
!expr_type.is_number() {
|
||||||
|
c.error('ref', field.pos)
|
||||||
|
}
|
||||||
|
struct_init.fields[i].typ = expr_type
|
||||||
|
struct_init.fields[i].expected_type = info_field.typ
|
||||||
}
|
}
|
||||||
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() &&
|
|
||||||
!expr_type.is_number() {
|
|
||||||
c.error('ref', field.pos)
|
|
||||||
}
|
|
||||||
struct_init.fields[i].typ = expr_type
|
|
||||||
struct_init.fields[i].expected_type = info_field.typ
|
|
||||||
}
|
}
|
||||||
// Check uninitialized refs
|
// Check uninitialized refs
|
||||||
for field in info.fields {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if field.typ.is_ptr() && !c.pref.translated {
|
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 {
|
match typ_sym.kind {
|
||||||
.struct_ {
|
.struct_ {
|
||||||
struct_info := typ_sym.info as table.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)
|
type_str := c.table.type_to_str(expr.expr_type)
|
||||||
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
||||||
return '', 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())
|
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 &&
|
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 {
|
left_type_sym.mod != '' { // method.mod != c.mod {
|
||||||
// If a private method is called outside of the module
|
// 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
|
// 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)
|
call_expr.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic)
|
||||||
} else {
|
} else {
|
||||||
|
// note: correct receiver type is automatically set here on struct embed calls
|
||||||
call_expr.receiver_type = method.params[0].typ
|
call_expr.receiver_type = method.params[0].typ
|
||||||
}
|
}
|
||||||
call_expr.return_type = method.return_type
|
call_expr.return_type = method.return_type
|
||||||
return 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
|
// TODO: str methods
|
||||||
if method_name == 'str' {
|
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`'
|
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 {
|
if sym.mod != c.mod && !field.is_pub && sym.language != .c {
|
||||||
c.error('field `${sym.name}.$field_name` is not public', selector_expr.pos)
|
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
|
selector_expr.typ = field.typ
|
||||||
return field.typ
|
return field.typ
|
||||||
} else {
|
|
||||||
if sym.kind == .aggregate {
|
|
||||||
unknown_field_msg = err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if sym.kind !in [.struct_, .aggregate] {
|
if sym.kind !in [.struct_, .aggregate] {
|
||||||
if sym.kind != .placeholder {
|
if sym.kind != .placeholder {
|
||||||
c.error('`$sym.name` is not a struct', selector_expr.pos)
|
c.error('`$sym.name` is not a struct', selector_expr.pos)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if sym.kind == .struct_ {
|
if sym.info is table.Struct {
|
||||||
sss := sym.info as table.Struct
|
suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name))
|
||||||
suggestion := util.new_suggestion(field_name, sss.fields.map(it.name))
|
|
||||||
c.error(suggestion.say(unknown_field_msg), selector_expr.pos)
|
c.error(suggestion.say(unknown_field_msg), selector_expr.pos)
|
||||||
}
|
}
|
||||||
c.error(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
|
max_type = ft.len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for field in node.fields.filter(it.is_embed) {
|
for embed in node.embeds {
|
||||||
f.writeln('\t$field.name')
|
styp := f.table.type_to_str(embed.typ)
|
||||||
|
f.writeln('\t$styp')
|
||||||
}
|
}
|
||||||
for i, field in node.fields {
|
for i, field in node.fields {
|
||||||
if field.is_embed {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if i == node.mut_pos {
|
if i == node.mut_pos {
|
||||||
f.writeln('mut:')
|
f.writeln('mut:')
|
||||||
} else if i == node.pub_pos {
|
} else if i == node.pub_pos {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
struct Foo {
|
|
||||||
x int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Test {}
|
|
||||||
|
|
||||||
struct Bar {
|
|
||||||
y int
|
|
||||||
Foo
|
|
||||||
z string
|
|
||||||
Test
|
|
||||||
}
|
|
|
@ -7,6 +7,6 @@ struct Test {}
|
||||||
struct Bar {
|
struct Bar {
|
||||||
Foo
|
Foo
|
||||||
Test
|
Test
|
||||||
y int
|
y int
|
||||||
z string
|
z string
|
||||||
}
|
}
|
|
@ -2664,93 +2664,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
g.struct_init(node)
|
g.struct_init(node)
|
||||||
}
|
}
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
|
g.selector_expr(node)
|
||||||
g.prevent_sum_type_unwrapping_once = false
|
|
||||||
if node.name_type > 0 {
|
|
||||||
g.type_name(node.name_type)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if node.expr_type == 0 {
|
|
||||||
g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos)
|
|
||||||
}
|
|
||||||
sym := g.table.get_type_symbol(node.expr_type)
|
|
||||||
// if node expr is a root ident and an optional
|
|
||||||
mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional)
|
|
||||||
if is_optional {
|
|
||||||
opt_base_typ := g.base_type(node.expr_type)
|
|
||||||
g.writeln('(*($opt_base_typ*)')
|
|
||||||
}
|
|
||||||
if sym.kind == .array_fixed {
|
|
||||||
assert node.field_name == 'len'
|
|
||||||
info := sym.info as table.ArrayFixed
|
|
||||||
g.write('$info.size')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if sym.kind == .chan && node.field_name == 'len' {
|
|
||||||
g.write('sync__Channel_len(')
|
|
||||||
g.expr(node.expr)
|
|
||||||
g.write(')')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mut sum_type_deref_field := ''
|
|
||||||
if f := g.table.struct_find_field(sym, node.field_name) {
|
|
||||||
field_sym := g.table.get_type_symbol(f.typ)
|
|
||||||
if field_sym.kind == .sum_type {
|
|
||||||
if !prevent_sum_type_unwrapping_once {
|
|
||||||
// check first if field is sum type because scope searching is expensive
|
|
||||||
scope := g.file.scope.innermost(node.pos.pos)
|
|
||||||
if field := scope.find_struct_field(node.expr_type, node.field_name) {
|
|
||||||
// union sum type deref
|
|
||||||
for i, typ in field.sum_type_casts {
|
|
||||||
g.write('(*')
|
|
||||||
cast_sym := g.table.get_type_symbol(typ)
|
|
||||||
if i != 0 {
|
|
||||||
dot := if field.typ.is_ptr() { '->' } else { '.' }
|
|
||||||
sum_type_deref_field += ')$dot'
|
|
||||||
}
|
|
||||||
if mut cast_sym.info is table.Aggregate {
|
|
||||||
agg_sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx])
|
|
||||||
sum_type_deref_field += '_$agg_sym.cname'
|
|
||||||
// sum_type_deref_field += '_${cast_sym.info.types[g.aggregate_type_idx]}'
|
|
||||||
} else {
|
|
||||||
sum_type_deref_field += '_$cast_sym.cname'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.expr(node.expr)
|
|
||||||
if is_optional {
|
|
||||||
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 node.expr_type.is_ptr() || sym.kind == .chan {
|
|
||||||
g.write('->')
|
|
||||||
} else {
|
|
||||||
// g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
|
|
||||||
g.write('.')
|
|
||||||
}
|
|
||||||
if node.expr_type.has_flag(.shared_f) {
|
|
||||||
g.write('val.')
|
|
||||||
}
|
|
||||||
if node.expr_type == 0 {
|
|
||||||
verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr')
|
|
||||||
}
|
|
||||||
g.write(c_name(node.field_name))
|
|
||||||
if sum_type_deref_field != '' {
|
|
||||||
g.write('.$sum_type_deref_field)')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.Type {
|
ast.Type {
|
||||||
// match sum Type
|
// match sum Type
|
||||||
|
@ -2813,6 +2727,93 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
g.type_name(node.name_type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if node.expr_type == 0 {
|
||||||
|
g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos)
|
||||||
|
}
|
||||||
|
sym := g.table.get_type_symbol(node.expr_type)
|
||||||
|
// if node expr is a root ident and an optional
|
||||||
|
mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional)
|
||||||
|
if is_optional {
|
||||||
|
opt_base_typ := g.base_type(node.expr_type)
|
||||||
|
g.writeln('(*($opt_base_typ*)')
|
||||||
|
}
|
||||||
|
if sym.kind == .array_fixed {
|
||||||
|
assert node.field_name == 'len'
|
||||||
|
info := sym.info as table.ArrayFixed
|
||||||
|
g.write('$info.size')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sym.kind == .chan && node.field_name == 'len' {
|
||||||
|
g.write('sync__Channel_len(')
|
||||||
|
g.expr(node.expr)
|
||||||
|
g.write(')')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mut sum_type_deref_field := ''
|
||||||
|
if f := g.table.struct_find_field(sym, node.field_name) {
|
||||||
|
field_sym := g.table.get_type_symbol(f.typ)
|
||||||
|
if field_sym.kind == .sum_type {
|
||||||
|
if !prevent_sum_type_unwrapping_once {
|
||||||
|
// check first if field is sum type because scope searching is expensive
|
||||||
|
scope := g.file.scope.innermost(node.pos.pos)
|
||||||
|
if field := scope.find_struct_field(node.expr_type, node.field_name) {
|
||||||
|
// union sum type deref
|
||||||
|
for i, typ in field.sum_type_casts {
|
||||||
|
g.write('(*')
|
||||||
|
cast_sym := g.table.get_type_symbol(typ)
|
||||||
|
if i != 0 {
|
||||||
|
dot := if field.typ.is_ptr() { '->' } else { '.' }
|
||||||
|
sum_type_deref_field += ')$dot'
|
||||||
|
}
|
||||||
|
if mut cast_sym.info is table.Aggregate {
|
||||||
|
agg_sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
|
sum_type_deref_field += '_$agg_sym.cname'
|
||||||
|
// sum_type_deref_field += '_${cast_sym.info.types[g.aggregate_type_idx]}'
|
||||||
|
} else {
|
||||||
|
sum_type_deref_field += '_$cast_sym.cname'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.expr(node.expr)
|
||||||
|
if is_optional {
|
||||||
|
g.write('.data)')
|
||||||
|
}
|
||||||
|
// struct embedding
|
||||||
|
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 {
|
||||||
|
g.write('->')
|
||||||
|
} else {
|
||||||
|
// g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
|
||||||
|
g.write('.')
|
||||||
|
}
|
||||||
|
if node.expr_type.has_flag(.shared_f) {
|
||||||
|
g.write('val.')
|
||||||
|
}
|
||||||
|
if node.expr_type == 0 {
|
||||||
|
verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr')
|
||||||
|
}
|
||||||
|
g.write(c_name(node.field_name))
|
||||||
|
if sum_type_deref_field != '' {
|
||||||
|
g.write('.$sum_type_deref_field)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g Gen) enum_expr(node ast.Expr) {
|
fn (mut g Gen) enum_expr(node ast.Expr) {
|
||||||
match node {
|
match node {
|
||||||
ast.EnumVal { g.write(node.val) }
|
ast.EnumVal { g.write(node.val) }
|
||||||
|
@ -4347,16 +4348,6 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
mut initialized := false
|
mut initialized := false
|
||||||
for i, field in struct_init.fields {
|
for i, field in struct_init.fields {
|
||||||
inited_fields[field.name] = i
|
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_ {
|
if sym.kind != .struct_ {
|
||||||
field_name := c_name(field.name)
|
field_name := c_name(field.name)
|
||||||
g.write('.$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 {
|
if info.is_union && struct_init.fields.len > 1 {
|
||||||
verror('union must not have more than 1 initializer')
|
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)
|
// g.zero_struct_fields(info, inited_fields)
|
||||||
// nr_fields = info.fields.len
|
// nr_fields = info.fields.len
|
||||||
for field in info.fields {
|
for field in info.fields {
|
||||||
|
@ -4404,10 +4412,6 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
if equal_fields.len == 0 {
|
if equal_fields.len == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tfield := equal_fields[0]
|
|
||||||
if tfield.embed_alias_for.len != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if field.name in inited_fields {
|
if field.name in inited_fields {
|
||||||
sfield := struct_init.fields[inited_fields[field.name]]
|
sfield := struct_init.fields[inited_fields[field.name]]
|
||||||
|
@ -4792,8 +4796,8 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
||||||
} else {
|
} else {
|
||||||
g.type_definitions.writeln('struct $name {')
|
g.type_definitions.writeln('struct $name {')
|
||||||
}
|
}
|
||||||
if typ.info.fields.len > 0 {
|
if typ.info.fields.len > 0 || typ.info.embeds.len > 0 {
|
||||||
for field in typ.info.fields.filter(it.embed_alias_for == '') {
|
for field in typ.info.fields {
|
||||||
// Some of these structs may want to contain
|
// Some of these structs may want to contain
|
||||||
// optionals that may not be defined at this point
|
// optionals that may not be defined at this point
|
||||||
// if this is the case then we are going to
|
// 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 {
|
// if info.is_interface {
|
||||||
// continue
|
// 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 {
|
for field in t.info.fields {
|
||||||
dep := g.table.get_type_symbol(field.typ).name
|
dep := g.table.get_type_symbol(field.typ).name
|
||||||
// skip if not in types list or already in deps
|
// 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)
|
g.write('/*af receiver arg*/' + arg_name)
|
||||||
} else {
|
} else {
|
||||||
g.expr(node.left)
|
g.expr(node.left)
|
||||||
|
if node.from_embed_type != 0 {
|
||||||
|
embed_name := typ_sym.embed_name()
|
||||||
|
g.write('.$embed_name')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if has_cast {
|
if has_cast {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
|
|
|
@ -77,7 +77,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
// println('struct decl $name')
|
// println('struct decl $name')
|
||||||
mut ast_fields := []ast.StructField{}
|
mut ast_fields := []ast.StructField{}
|
||||||
mut fields := []table.Field{}
|
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 mut_pos := -1
|
||||||
mut pub_pos := -1
|
mut pub_pos := -1
|
||||||
mut pub_mut_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()
|
field_start_pos := p.tok.position()
|
||||||
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) ||
|
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) ||
|
||||||
p.peek_tok.kind == .dot) &&
|
p.peek_tok.kind == .dot) &&
|
||||||
language == .v
|
language == .v && ast_fields.len == 0
|
||||||
mut field_name := ''
|
mut field_name := ''
|
||||||
mut typ := table.Type(0)
|
mut typ := table.Type(0)
|
||||||
mut type_pos := token.Position{}
|
mut type_pos := token.Position{}
|
||||||
mut field_pos := token.Position{}
|
mut field_pos := token.Position{}
|
||||||
if is_embed {
|
if is_embed {
|
||||||
// struct embedding
|
// struct embedding
|
||||||
|
type_pos = p.tok.position()
|
||||||
typ = p.parse_type()
|
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 {
|
for p.tok.kind == .comment {
|
||||||
comments << p.comment()
|
comments << p.comment()
|
||||||
if p.tok.kind == .rcbr {
|
if p.tok.kind == .rcbr {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type_pos = p.prev_tok.position()
|
type_pos = type_pos.extend(p.prev_tok.position())
|
||||||
field_pos = p.prev_tok.position()
|
sym := p.table.get_type_symbol(typ)
|
||||||
field_name = symbol_name
|
if typ in embed_types {
|
||||||
if typ in embedded_structs {
|
p.error_with_pos('cannot embed `$sym.name` more than once', type_pos)
|
||||||
p.error_with_pos('cannot embed `$field_name` more than once', type_pos)
|
|
||||||
return ast.StructDecl{}
|
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 {
|
} else {
|
||||||
// struct field
|
// struct field
|
||||||
field_name = p.check_name()
|
field_name = p.check_name()
|
||||||
|
@ -225,20 +230,20 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
}
|
}
|
||||||
has_default_expr = true
|
has_default_expr = true
|
||||||
}
|
}
|
||||||
|
// TODO merge table and ast Fields?
|
||||||
|
ast_fields << ast.StructField{
|
||||||
|
name: field_name
|
||||||
|
pos: field_pos
|
||||||
|
type_pos: type_pos
|
||||||
|
typ: typ
|
||||||
|
comments: comments
|
||||||
|
default_expr: default_expr
|
||||||
|
has_default_expr: has_default_expr
|
||||||
|
attrs: p.attrs
|
||||||
|
is_public: is_field_pub
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO merge table and ast Fields?
|
// save embeds as table fields too, it will be used in generation phase
|
||||||
ast_fields << ast.StructField{
|
|
||||||
name: field_name
|
|
||||||
pos: field_pos
|
|
||||||
type_pos: type_pos
|
|
||||||
typ: typ
|
|
||||||
comments: comments
|
|
||||||
default_expr: default_expr
|
|
||||||
has_default_expr: has_default_expr
|
|
||||||
attrs: p.attrs
|
|
||||||
is_public: is_field_pub
|
|
||||||
is_embed: is_embed
|
|
||||||
}
|
|
||||||
fields << table.Field{
|
fields << table.Field{
|
||||||
name: field_name
|
name: field_name
|
||||||
typ: typ
|
typ: typ
|
||||||
|
@ -248,7 +253,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
is_mut: is_field_mut
|
is_mut: is_field_mut
|
||||||
is_global: is_field_global
|
is_global: is_field_global
|
||||||
attrs: p.attrs
|
attrs: p.attrs
|
||||||
is_embed: is_embed
|
|
||||||
}
|
}
|
||||||
p.attrs = []
|
p.attrs = []
|
||||||
}
|
}
|
||||||
|
@ -270,6 +274,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
cname: util.no_dots(name)
|
cname: util.no_dots(name)
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
info: table.Struct{
|
info: table.Struct{
|
||||||
|
embeds: embed_types
|
||||||
fields: fields
|
fields: fields
|
||||||
is_typedef: attrs.contains('typedef')
|
is_typedef: attrs.contains('typedef')
|
||||||
is_union: is_union
|
is_union: is_union
|
||||||
|
@ -301,6 +306,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
attrs: attrs
|
attrs: attrs
|
||||||
end_comments: end_comments
|
end_comments: end_comments
|
||||||
gen_types: generic_types
|
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 struct Struct {
|
||||||
pub mut:
|
pub mut:
|
||||||
|
embeds []Type
|
||||||
fields []Field
|
fields []Field
|
||||||
is_typedef bool // C. [typedef]
|
is_typedef bool // C. [typedef]
|
||||||
is_union bool
|
is_union bool
|
||||||
|
@ -663,8 +664,6 @@ pub mut:
|
||||||
is_pub bool
|
is_pub bool
|
||||||
is_mut bool
|
is_mut bool
|
||||||
is_global 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 {
|
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()
|
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 {
|
pub fn (t &TypeSymbol) has_method(name string) bool {
|
||||||
t.find_method(name) or { return false }
|
t.find_method(name) or { return false }
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -7,7 +7,6 @@ struct Foo {
|
||||||
|
|
||||||
fn (f Foo) foo() {}
|
fn (f Foo) foo() {}
|
||||||
|
|
||||||
|
|
||||||
struct Bar {
|
struct Bar {
|
||||||
Foo
|
Foo
|
||||||
}
|
}
|
||||||
|
@ -15,7 +14,7 @@ struct Bar {
|
||||||
fn test_embed() {
|
fn test_embed() {
|
||||||
b := Bar{}
|
b := Bar{}
|
||||||
assert b.x == 0
|
assert b.x == 0
|
||||||
//b.foo() // TODO methods
|
b.foo()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_embed_direct_access() {
|
fn test_embed_direct_access() {
|
||||||
|
@ -27,7 +26,7 @@ fn test_default_value() {
|
||||||
b := Bar{Foo: Foo{}}
|
b := Bar{Foo: Foo{}}
|
||||||
assert b.y == 5
|
assert b.y == 5
|
||||||
}
|
}
|
||||||
/*
|
/* TODO
|
||||||
fn test_initialize() {
|
fn test_initialize() {
|
||||||
b := Bar{x: 1, y: 2}
|
b := Bar{x: 1, y: 2}
|
||||||
assert b.x == 1
|
assert b.x == 1
|
||||||
|
@ -59,5 +58,19 @@ fn test_generic_embed() {
|
||||||
b := BarGenericContainer{}
|
b := BarGenericContainer{}
|
||||||
assert b.BarGeneric.foo == 0
|
assert b.BarGeneric.foo == 0
|
||||||
assert b.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