Skip to content

Commit

Permalink
feat: add slice expression type checking
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nrwiersma committed Aug 19, 2020
1 parent 065d4fa commit 1029d10
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 2 deletions.
6 changes: 6 additions & 0 deletions interp/cfg.go
Expand Up @@ -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
}
Expand Down
27 changes: 27 additions & 0 deletions interp/interp_eval_test.go
Expand Up @@ -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{
Expand Down
94 changes: 92 additions & 2 deletions interp/typecheck.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 1029d10

Please sign in to comment.