From 352e353ff93135716776cec5c4bfe5c6735e5d80 Mon Sep 17 00:00:00 2001 From: UladzimirTrehubenka Date: Sat, 13 Dec 2025 21:06:25 +0300 Subject: [PATCH 1/2] Fix vartuple handling for nested objects --- gen/decode.go | 6 +++-- gen/unmarshal.go | 6 +++-- issue430_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 issue430_test.go diff --git a/gen/decode.go b/gen/decode.go index 6e1af0ee..bb720f87 100644 --- a/gen/decode.go +++ b/gen/decode.go @@ -201,7 +201,8 @@ func (d *decodeGen) structAsTuple(s *Struct) { d.p.declare(sz, u32) d.assignArray(sz, arrayHeader, 0) if s.AsVarTuple { - d.p.printf("\nif %[1]s == 0 { return }", sz) + d.p.printf("\nfor range 1 {") // start the block + d.p.printf("\nif %[1]s == 0 { break }", sz) // exit from the block } else { d.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz) } @@ -239,11 +240,12 @@ func (d *decodeGen) structAsTuple(s *Struct) { d.p.printf("\n}") // close if statement } if s.AsVarTuple { - d.p.printf("\nif %[1]s--; %[1]s == 0 { return }", sz) + d.p.printf("\nif %[1]s--; %[1]s == 0 { break }", sz) // exit from the block } } if s.AsVarTuple { d.p.printf("\nfor ; %[1]s > 0; %[1]s-- {\nif err = dc.Skip(); err != nil {\nerr = msgp.WrapError(err)\nreturn\n}\n}", sz) + d.p.printf("\n}") // end the block } } diff --git a/gen/unmarshal.go b/gen/unmarshal.go index f74b6180..32d0188a 100644 --- a/gen/unmarshal.go +++ b/gen/unmarshal.go @@ -223,7 +223,8 @@ func (u *unmarshalGen) tuple(s *Struct) { u.p.declare(sz, u32) u.assignAndCheck(sz, arrayHeader) if s.AsVarTuple { - u.p.printf("\nif %[1]s == 0 {\no = bts\nreturn\n}", sz) + u.p.printf("\nfor range 1 {") // start the block + u.p.printf("\nif %[1]s == 0 {\no = bts\nbreak\n}", sz) // exit from the block } else { u.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz) } @@ -265,11 +266,12 @@ func (u *unmarshalGen) tuple(s *Struct) { u.p.printf("\n}") } if s.AsVarTuple { - u.p.printf("\nif %[1]s--; %[1]s == 0 {\no = bts\nreturn\n}", sz) + u.p.printf("\nif %[1]s--; %[1]s == 0 {\no = bts\nbreak\n}", sz) // exit from the block } } if s.AsVarTuple { u.p.printf("\nfor ; %[1]s > 0; %[1]s-- {\nbts, err = msgp.Skip(bts)\nif err != nil {\nerr = msgp.WrapError(err)\nreturn\n}\n}", sz) + u.p.printf("\n}") // end the block } } diff --git a/issue430_test.go b/issue430_test.go new file mode 100644 index 00000000..55ac026d --- /dev/null +++ b/issue430_test.go @@ -0,0 +1,60 @@ +package main + +import "testing" + +func TestIssue430Nested(t *testing.T) { + mainFilename, err := generate(t, issue430Nested) + if err != nil { + t.Fatalf("generate failed: %v", err) + } + + goExec(t, mainFilename, false) // exec go run + goExec(t, mainFilename, true) // exec go test +} + +var issue430Nested = `package main + +import ( + "fmt" + "os" +) + +//go:generate msgp + +//msgp:vartuple Bag +type Bag struct { + ItemID uint32 + ItemNum uint32 +} + +//msgp:vartuple User +type User struct { + Name string + Bag []*Bag +} + +func main() { + u := User{Name: "user", Bag: []*Bag{{1, 2}, {3, 4}}} + data, err := u.MarshalMsg(nil) + if err != nil { + fmt.Println("User MarshalMsg failed:", err) + os.Exit(1) + } + + var user User + if _, err := user.UnmarshalMsg(data); err != nil { + fmt.Println("User UnmarshalMsg failed:", err) + os.Exit(1) + } + if user.Name != "user" || + user.Bag[0] == nil || + user.Bag[0].ItemID != 1 || + user.Bag[0].ItemNum != 2 || + user.Bag[1] == nil || + user.Bag[1].ItemID != 3 || + user.Bag[1].ItemNum != 4 { + fmt.Println("User UnmarshalMsg bad user:", user) + os.Exit(1) + } +} +` From bcdbba76ccda62c23eef47b29633f891321da93a Mon Sep 17 00:00:00 2001 From: UladzimirTrehubenka Date: Sat, 13 Dec 2025 22:48:29 +0300 Subject: [PATCH 2/2] Address comments --- gen/decode.go | 3 +-- gen/unmarshal.go | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/gen/decode.go b/gen/decode.go index bb720f87..2b6a6f38 100644 --- a/gen/decode.go +++ b/gen/decode.go @@ -201,8 +201,7 @@ func (d *decodeGen) structAsTuple(s *Struct) { d.p.declare(sz, u32) d.assignArray(sz, arrayHeader, 0) if s.AsVarTuple { - d.p.printf("\nfor range 1 {") // start the block - d.p.printf("\nif %[1]s == 0 { break }", sz) // exit from the block + d.p.printf("\nfor %[1]s > 0 {", sz) // start the block } else { d.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz) } diff --git a/gen/unmarshal.go b/gen/unmarshal.go index 32d0188a..21f30b54 100644 --- a/gen/unmarshal.go +++ b/gen/unmarshal.go @@ -223,8 +223,7 @@ func (u *unmarshalGen) tuple(s *Struct) { u.p.declare(sz, u32) u.assignAndCheck(sz, arrayHeader) if s.AsVarTuple { - u.p.printf("\nfor range 1 {") // start the block - u.p.printf("\nif %[1]s == 0 {\no = bts\nbreak\n}", sz) // exit from the block + u.p.printf("\nfor %[1]s > 0 {", sz) // start the block } else { u.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz) } @@ -266,7 +265,7 @@ func (u *unmarshalGen) tuple(s *Struct) { u.p.printf("\n}") } if s.AsVarTuple { - u.p.printf("\nif %[1]s--; %[1]s == 0 {\no = bts\nbreak\n}", sz) // exit from the block + u.p.printf("\nif %[1]s--; %[1]s == 0 { break }", sz) // exit from the block } } if s.AsVarTuple {