Skip to content

Commit 1a8cfe4

Browse files
author
Tural Devrishev
committed
compiler: fix struct field increment
Close #3981. Signed-off-by: Tural Devrishev <tural@nspcc.ru>
1 parent 01aab78 commit 1a8cfe4

File tree

3 files changed

+139
-22
lines changed

3 files changed

+139
-22
lines changed

pkg/compiler/codegen.go

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,29 @@ func (c *codegen) emitStoreStructField(i int) {
238238
emit.Opcodes(c.prog.BinWriter, opcode.ROT, opcode.SETITEM)
239239
}
240240

241+
func (c *codegen) emitStoreSelectorExpr(n *ast.SelectorExpr) {
242+
typ := c.typeOf(n.X)
243+
if c.isInvalidType(typ) {
244+
// This is a global variable from a package.
245+
c.emitStoreVar(n.X.(*ast.Ident).Name, n.Sel.Name)
246+
return
247+
}
248+
strct, ok := c.getStruct(typ)
249+
if !ok {
250+
c.prog.Err = fmt.Errorf("nested selector assigns not supported yet")
251+
return
252+
}
253+
ast.Walk(c, n.X)
254+
i := indexOfStruct(strct, n.Sel.Name)
255+
c.emitStoreStructField(i)
256+
}
257+
258+
func (c *codegen) emitStoreIndexExpr(n *ast.IndexExpr) {
259+
ast.Walk(c, n.X)
260+
ast.Walk(c, n.Index)
261+
emit.Opcodes(c.prog.BinWriter, opcode.ROT, opcode.SETITEM)
262+
}
263+
241264
// getVarIndex returns variable type and position in the corresponding slot,
242265
// according to the current scope.
243266
func (c *codegen) getVarIndex(pkg string, name string) *varInfo {
@@ -711,30 +734,15 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
711734
if !isAssignOp && !isMapKeyCheck {
712735
ast.Walk(c, n.Rhs[i])
713736
}
714-
typ := c.typeOf(t.X)
715-
if c.isInvalidType(typ) {
716-
// Store to other package global variable.
717-
c.emitStoreVar(t.X.(*ast.Ident).Name, t.Sel.Name)
718-
return nil
719-
}
720-
strct, ok := c.getStruct(typ)
721-
if !ok {
722-
c.prog.Err = fmt.Errorf("nested selector assigns not supported yet")
723-
return nil
724-
}
725-
ast.Walk(c, t.X) // load the struct
726-
i := indexOfStruct(strct, t.Sel.Name) // get the index of the field
727-
c.emitStoreStructField(i) // store the field
737+
c.emitStoreSelectorExpr(t)
728738

729739
// Assignments to index expressions.
730740
// slice[0] = 10
731741
case *ast.IndexExpr:
732742
if !isAssignOp && !isMapKeyCheck {
733743
ast.Walk(c, n.Rhs[i])
734744
}
735-
ast.Walk(c, t.X)
736-
ast.Walk(c, t.Index)
737-
emit.Opcodes(c.prog.BinWriter, opcode.ROT, opcode.SETITEM)
745+
c.emitStoreIndexExpr(t)
738746
}
739747
}
740748
return nil
@@ -1207,11 +1215,17 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
12071215
ast.Walk(c, n.X)
12081216
c.emitToken(n.Tok, c.typeOf(n.X))
12091217

1210-
// For now, only identifiers are supported for (post) for stmts.
1211-
// for i := 0; i < 10; i++ {}
1212-
// Where the post stmt is ( i++ )
1213-
if ident, ok := n.X.(*ast.Ident); ok {
1214-
c.emitStoreVar("", ident.Name)
1218+
switch t := n.X.(type) {
1219+
case *ast.Ident:
1220+
c.emitStoreVar("", t.Name)
1221+
1222+
case *ast.SelectorExpr:
1223+
// myStruct.t--
1224+
c.emitStoreSelectorExpr(t)
1225+
1226+
case *ast.IndexExpr:
1227+
// slice[0]++
1228+
c.emitStoreIndexExpr(t)
12151229
}
12161230
return nil
12171231

pkg/compiler/slice_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,60 @@ var sliceTestCases = []testCase{
338338
`,
339339
[]byte("str"),
340340
},
341+
{
342+
"increment elem, alias",
343+
`func F%d() int {
344+
s := []int{0}
345+
t := s
346+
t[0]++
347+
return s[0]
348+
}
349+
`,
350+
big.NewInt(1),
351+
},
352+
{
353+
"increment elem, struct",
354+
`type T struct { V int }
355+
func F%d() int {
356+
s := []T{{0}}
357+
s[0].V++
358+
return s[0].V
359+
}
360+
`,
361+
big.NewInt(1),
362+
},
363+
{
364+
"increment elem, pointer",
365+
`func F%d() int {
366+
t := &T{0}
367+
s := []*T{t}
368+
s[0].V++
369+
return t.V
370+
}
371+
`,
372+
big.NewInt(1),
373+
},
374+
{
375+
"increment elem, 2d slice",
376+
`func F%d() int {
377+
s := [][]int{{0}}
378+
s[0][0]++
379+
return s[0][0]
380+
}
381+
`,
382+
big.NewInt(1),
383+
},
384+
{
385+
"increment elem, func call",
386+
`func f(s []int) []int { return s }
387+
func F%d() int {
388+
var s = []int{0}
389+
f(s)[0]++
390+
return s[0]
391+
}
392+
`,
393+
big.NewInt(1),
394+
},
341395
}
342396

343397
func TestSliceOperations(t *testing.T) {

pkg/compiler/struct_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,55 @@ var structTestCases = []testCase{
431431
`,
432432
big.NewInt(2),
433433
},
434+
{
435+
"increment field, simple",
436+
`type test struct { t int }
437+
func F%d() int {
438+
var myStruct test
439+
myStruct.t++
440+
return myStruct.t
441+
}
442+
`,
443+
big.NewInt(1),
444+
},
445+
{
446+
"increment field, nested",
447+
`type nested struct { t int }
448+
type external struct { n nested }
449+
func F%d() int {
450+
var myStruct external
451+
myStruct.n.t++
452+
return myStruct.n.t
453+
}
454+
`,
455+
big.NewInt(1),
456+
},
457+
{
458+
"increment field, selector chain",
459+
`type C struct { v int }
460+
type B struct { c C }
461+
type A struct { b B }
462+
func F%d() int {
463+
var a A
464+
a.b.c.v++
465+
return a.b.c.v
466+
}
467+
`,
468+
big.NewInt(1),
469+
},
470+
{
471+
"incremental field, func call",
472+
`func f(t *test) *test {
473+
return t
474+
}
475+
func F%d() int {
476+
var myStruct = &test{}
477+
f(myStruct).t++
478+
return myStruct.t
479+
}
480+
`,
481+
big.NewInt(1),
482+
},
434483
}
435484

436485
func TestStructs(t *testing.T) {

0 commit comments

Comments
 (0)