Skip to content

Commit 1ba8021

Browse files
authored
v: implement assignable anon struct (fix #23855) (#23857)
1 parent 95f53a3 commit 1ba8021

File tree

6 files changed

+84
-1
lines changed

6 files changed

+84
-1
lines changed

vlib/v/checker/assign.v

+6
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
199199
}
200200
c.inside_decl_rhs = is_decl
201201
mut expr := node.right[i]
202+
if left is ast.Ident && left.is_mut() && expr is ast.StructInit && expr.is_anon {
203+
c.anon_struct_should_be_mut = true
204+
defer {
205+
c.anon_struct_should_be_mut = false
206+
}
207+
}
202208
right_type := c.expr(mut expr)
203209
c.inside_decl_rhs = false
204210
c.inside_ref_lit = old_inside_ref_lit

vlib/v/checker/checker.v

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub mut:
8989
inside_fn_arg bool // `a`, `b` in `a.f(b)`
9090
inside_ct_attr bool // true inside `[if expr]`
9191
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
92+
anon_struct_should_be_mut bool // true when `mut var := struct { ... }` is used
9293
inside_generic_struct_init bool
9394
inside_integer_literal_cast bool // true inside `int(123)`
9495
cur_struct_generic_types []ast.Type

vlib/v/checker/fn.v

+5
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
16591659
}
16601660
}
16611661
continue
1662+
} else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct
1663+
&& param_typ_sym.info.is_anon {
1664+
if c.is_anon_struct_compatible(param_typ_sym.info, arg_typ_sym.info) {
1665+
continue
1666+
}
16621667
}
16631668
if c.pref.translated || c.file.is_translated {
16641669
// in case of variadic make sure to use array elem type for checks

vlib/v/checker/struct.v

+44-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
534534
&& c.table.cur_concrete_types.len == 0 {
535535
pos := type_sym.name.last_index_u8(`.`)
536536
first_letter := type_sym.name[pos + 1]
537-
if !first_letter.is_capital()
537+
if !first_letter.is_capital() && type_sym.kind != .none
538538
&& (type_sym.kind != .struct || !(type_sym.info is ast.Struct && type_sym.info.is_anon))
539539
&& type_sym.kind != .placeholder {
540540
c.error('cannot initialize builtin type `${type_sym.name}`', node.pos)
@@ -850,6 +850,35 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
850850
inited_fields)
851851
}
852852
}
853+
.none {
854+
// var := struct { name: "" }
855+
mut init_fields := []ast.StructField{}
856+
for init_field in node.init_fields {
857+
mut expr := unsafe { init_field }
858+
init_fields << ast.StructField{
859+
name: init_field.name
860+
typ: c.expr(mut expr.expr)
861+
is_mut: c.anon_struct_should_be_mut
862+
}
863+
}
864+
c.table.anon_struct_counter++
865+
name := '_VAnonStruct${c.table.anon_struct_counter}'
866+
sym_struct := ast.TypeSymbol{
867+
kind: .struct
868+
language: .v
869+
name: name
870+
cname: util.no_dots(name)
871+
mod: c.mod
872+
info: ast.Struct{
873+
is_anon: true
874+
fields: init_fields
875+
}
876+
is_pub: true
877+
}
878+
ret := c.table.register_sym(sym_struct)
879+
c.table.register_anon_struct(name, ret)
880+
node.typ = c.table.find_type_idx(name)
881+
}
853882
else {}
854883
}
855884
if node.has_update_expr {
@@ -1140,3 +1169,17 @@ fn (mut c Checker) check_ref_fields_initialized_note(struct_sym &ast.TypeSymbol,
11401169
}
11411170
}
11421171
}
1172+
1173+
fn (mut c Checker) is_anon_struct_compatible(s1 ast.Struct, s2 ast.Struct) bool {
1174+
if !(s1.is_anon && s2.is_anon && s1.fields.len == s2.fields.len) {
1175+
return false
1176+
}
1177+
mut is_compatible := true
1178+
for k, field in s1.fields {
1179+
if !c.check_basic(field.typ, s2.fields[k].typ) {
1180+
is_compatible = false
1181+
break
1182+
}
1183+
}
1184+
return is_compatible
1185+
}

vlib/v/gen/c/fn.v

+3
Original file line numberDiff line numberDiff line change
@@ -2760,6 +2760,9 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as
27602760
g.expr_with_cast(arg.expr, arg_typ, expected_type)
27612761
g.write('.data')
27622762
return
2763+
} else if arg.expr is ast.Ident && arg_sym.info is ast.Struct && arg_sym.info.is_anon {
2764+
// make anon struct struct compatible with another anon struct declaration
2765+
g.write('*(${g.cc_type(expected_type, false)}*)&')
27632766
}
27642767
// check if the argument must be dereferenced or not
27652768
g.arg_no_auto_deref = is_smartcast && !arg_is_ptr && !exp_is_ptr && arg.should_be_ptr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
fn t() int {
2+
return 123
3+
}
4+
5+
fn r(a struct { name string age int }) {
6+
assert '${a}' == "struct {
7+
name: 'Foo'
8+
age: 123
9+
}"
10+
}
11+
12+
fn test_main() {
13+
mut a := struct {
14+
name: 'Foo'
15+
age: t()
16+
}
17+
dump(a)
18+
r(a)
19+
r(struct { name: 'Foo', age: t() })
20+
a.age = 2
21+
assert '${a}' == "struct {
22+
name: 'Foo'
23+
age: 2
24+
}"
25+
}

0 commit comments

Comments
 (0)