From 1029d102e5ba5e4f0ac6cd41d1968481c9160825 Mon Sep 17 00:00:00 2001 From: Nicholas Wiersma Date: Wed, 19 Aug 2020 16:04:05 +0200 Subject: [PATCH] feat: add slice expression type checking This adds type checking to SliceExpr as well as handling any required constant type conversion. It should be noted that the check that `high` and `max` are not nil in a 3-index slice has been left out as this check is handled before type checking. Tests have been included for these cases. --- interp/cfg.go | 6 +++ interp/interp_eval_test.go | 27 +++++++++++ interp/typecheck.go | 94 +++++++++++++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/interp/cfg.go b/interp/cfg.go index 800316922..d0f53a953 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -1692,6 +1692,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case sliceExpr: wireChild(n) + + err = check.sliceExpr(n) + if err != nil { + break + } + if n.typ, err = nodeType(interp, sc, n); err != nil { return } diff --git a/interp/interp_eval_test.go b/interp/interp_eval_test.go index a497bb95f..77f1c8dbd 100644 --- a/interp/interp_eval_test.go +++ b/interp/interp_eval_test.go @@ -355,6 +355,33 @@ func TestEvalCompositeStruct(t *testing.T) { }) } +func TestEvalSliceExpression(t *testing.T) { + i := interp.New(interp.Options{}) + runTests(t, i, []testCase{ + {src: `a := []int{0,1,2}[1:3]`, res: "[1 2]"}, + {src: `a := []int{0,1,2}[:3]`, res: "[0 1 2]"}, + {src: `a := []int{0,1,2}[:]`, res: "[0 1 2]"}, + {src: `a := []int{0,1,2,3}[1:3:4]`, res: "[1 2]"}, + {src: `a := []int{0,1,2,3}[:3:4]`, res: "[0 1 2]"}, + {src: `ar := [3]int{0,1,2} + a := ar[1:3]`, res: "[1 2]"}, + {src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"}, + {src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"}, + {src: `s := "hello"[1:3]`, res: "el"}, + {src: `str := "hello" + s := str[1:3]`, res: "el"}, + {src: `a := int(1)[0:1]`, err: "1:33: cannot slice type int"}, + {src: `a := ([3]int{0,1,2})[1:3]`, err: "1:33: cannot slice type [3]int"}, + {src: `a := (&[]int{0,1,2,3})[1:3]`, err: "1:33: cannot slice type *[]int"}, + {src: `a := "hello"[1:3:4]`, err: "1:45: invalid operation: 3-index slice of string"}, + {src: `ar := [3]int{0,1,2} + a := ar[:4]`, err: "2:16: index int is out of bounds"}, + {src: `a := []int{0,1,2,3}[1::4]`, err: "1:49: 2nd index required in 3-index slice"}, + {src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"}, + {src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"}, + }) +} + func TestEvalConversion(t *testing.T) { i := interp.New(interp.Options{}) runTests(t, i, []testCase{ diff --git a/interp/typecheck.go b/interp/typecheck.go index fba914f73..c8a9aad9f 100644 --- a/interp/typecheck.go +++ b/interp/typecheck.go @@ -318,7 +318,7 @@ func (check typecheck) mapLitExpr(child []*node, ktyp, vtyp *itype) error { return nil } -// structLitExpr type checks an struct composite literal expression. +// structLitExpr type checks a struct composite literal expression. func (check typecheck) structLitExpr(child []*node, typ *itype) error { if len(child) == 0 { return nil @@ -377,7 +377,7 @@ func (check typecheck) structLitExpr(child []*node, typ *itype) error { return nil } -// structBinLitExpr type checks an struct composite literal expression on a binary type. +// structBinLitExpr type checks a struct composite literal expression on a binary type. func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error { if len(child) == 0 { return nil @@ -437,6 +437,96 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error { return nil } +// sliceExpr type checks a slice expression. +func (check typecheck) sliceExpr(n *node) error { + c, child := n.child[0], n.child[1:] + + t := c.typ.TypeOf() + var low, high, max *node + if len(child) >= 1 { + if n.action == aSlice { + low = child[0] + } else { + high = child[0] + } + } + if len(child) >= 2 { + if n.action == aSlice { + high = child[1] + } else { + max = child[1] + } + } + if len(child) == 3 && n.action == aSlice { + max = child[2] + } + + l := -1 + valid := false + switch t.Kind() { + case reflect.String: + valid = true + if c.rval.IsValid() { + l = len(vString(c.rval)) + } + if max != nil { + return max.cfgErrorf("invalid operation: 3-index slice of string") + } + case reflect.Array: + valid = true + l = t.Len() + if c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) { + return c.cfgErrorf("cannot slice type %s", c.typ.id()) + } + case reflect.Slice: + valid = true + case reflect.Ptr: + if t.Elem().Kind() == reflect.Array { + valid = true + l = t.Elem().Len() + } + } + if !valid { + return c.cfgErrorf("cannot slice type %s", c.typ.id()) + } + + var ind [3]int64 + for i, nod := range []*node{low, high, max} { + x := int64(-1) + switch { + case nod != nil: + max := -1 + if l >= 0 { + max = l + 1 + } + if err := check.index(nod, max); err != nil { + return err + } + if nod.rval.IsValid() { + x = vInt(nod.rval) + } + case i == 0: + x = 0 + case l >= 0: + x = int64(l) + } + ind[i] = x + } + + for i, x := range ind[:len(ind)-1] { + if x <= 0 { + continue + } + for _, y := range ind[i+1:] { + if y < 0 || x <= y { + continue + } + return n.cfgErrorf("invalid index values, must be low <= high <= max") + } + } + return nil +} + // conversion type checks the conversion of n to typ. func (check typecheck) conversion(n *node, typ *itype) error { var c constant.Value