-
Notifications
You must be signed in to change notification settings - Fork 197
Description
Hi maintainers,
I believe I found a bug caused by the interaction of //msgp:vartuple and the inlining pass (parse/inline.go).
What I did
define.go:
//go:generate msgp -tests=false -o define_gen.go define.go
//msgp:vartuple Bag User
type Bag struct {
ItemID uint32
ItemNum uint32
}
type User struct {
Name string
Bag []*Bag
}
What I expected
User.UnmarshalMsg should iterate through Bag []*Bag and unmarshal all elements.
What happened
The generated User.UnmarshalMsg contains return statements inside the per-element decoding logic for Bag (vartuple). Since Bag gets inlined into User.UnmarshalMsg, those returns exit User.UnmarshalMsg early, so decoding stops and later slice elements are not processed.
Relevant generated snippet (inside for za0001 := range z.Bag { ... } ):
zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts)
...
if zb0003 == 0 {
o = bts
return
}
...
if zb0003--; zb0003 == 0 {
o = bts
return
}
This return is correct if it is inside (*Bag).UnmarshalMsg, but becomes incorrect when Bag is inlined into the outer User.UnmarshalMsg.
Why I think this happens
parse.File() always runs an inlining pass (fs.propInline()).
Inlining replaces small IDENT nodes with a copied gen.Struct.
The vartuple tuple-unmarshal codegen for structs emits early return statements when sz == 0 or after sz-- reaches 0.
When such a struct is inlined into a larger method (e.g., slice element decoding), the early return exits the outer method instead of only stopping the struct decoding.