From 7863456d52875f64e652c75980fe1d55705f3861 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 13 Apr 2021 18:00:12 +0200 Subject: [PATCH] interp: refactor slice type management Add missing `sliceT` type category for consistency. Remove `sizedef` field in `itype` struct. Rename field `size` to `length`. Clean the various hacks used to cope with the absence of `sliceT`. --- interp/cfg.go | 16 ++++------ interp/run.go | 20 ++++++------ interp/type.go | 75 ++++++++++++++++++++------------------------- interp/typecheck.go | 11 ++++--- 4 files changed, 58 insertions(+), 64 deletions(-) diff --git a/interp/cfg.go b/interp/cfg.go index 32991805c..7ad392bb2 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -179,7 +179,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range ktyp = sc.getType("int") vtyp = sc.getType("rune") - case arrayT, variadicT: + case arrayT, sliceT, variadicT: sc.add(sc.getType("int")) // Add a dummy type to store array shallow copy for range ktyp = sc.getType("int") vtyp = o.typ.val @@ -525,10 +525,6 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { if dest.typ.incomplete { return } - if dest.typ.sizedef { - dest.typ.size = arrayTypeLen(src) - dest.typ.rtype = nil - } if sc.global { // Do not overload existing symbols (defined in GTA) in global scope sym, _, _ = sc.lookup(dest.ident) @@ -1054,8 +1050,8 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { } switch n.typ.cat { - case arrayT: - err = check.arrayLitExpr(child, n.typ.val, n.typ.size) + case arrayT, sliceT: + err = check.arrayLitExpr(child, n.typ) case mapT: err = check.mapLitExpr(child, n.typ.key, n.typ.val) case structT: @@ -2513,7 +2509,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat switch typ.cat { case aliasT, ptrT: gen = compositeGenerator(n, n.typ.val, rtyp) - case arrayT: + case arrayT, sliceT: gen = arrayLit case mapT: gen = mapLit @@ -2567,8 +2563,8 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat // array variable it is determined from the value's type, otherwise it is // computed from the source definition. func arrayTypeLen(n *node) int { - if n.typ != nil && n.typ.sizedef { - return n.typ.size + if n.typ != nil && n.typ.cat == arrayT { + return n.typ.length } max := -1 for i, c := range n.child[1:] { diff --git a/interp/run.go b/interp/run.go index 24b71db53..521da2820 100644 --- a/interp/run.go +++ b/interp/run.go @@ -571,7 +571,7 @@ func isRecursiveType(t *itype, rtype reflect.Type) bool { return true } switch t.cat { - case ptrT, arrayT, mapT: + case arrayT, mapT, ptrT, sliceT: return isRecursiveType(t.val, t.val.rtype) default: return false @@ -1409,7 +1409,7 @@ func callBin(n *node) { } // empty interface, do not wrap it. values = append(values, genValue(c)) - case arrayT, variadicT: + case arrayT, sliceT, variadicT: switch c.typ.val.cat { case interfaceT: if len(c.typ.val.field) > 0 { @@ -2340,12 +2340,13 @@ func arrayLit(n *node) { } typ := n.typ.frameType() + kind := typ.Kind() n.exec = func(f *frame) bltn { var a reflect.Value - if n.typ.sizedef { - a, _ = n.typ.zero() - } else { + if kind == reflect.Slice { a = reflect.MakeSlice(typ, max, max) + } else { + a, _ = n.typ.zero() } for i, v := range values { a.Index(index[i]).Set(v(f)) @@ -2452,12 +2453,13 @@ func compositeBinSlice(n *node) { } typ := n.typ.frameType() + kind := typ.Kind() n.exec = func(f *frame) bltn { var a reflect.Value - if n.typ.sizedef { - a, _ = n.typ.zero() - } else { + if kind == reflect.Slice { a = reflect.MakeSlice(typ, max, max) + } else { + a, _ = n.typ.zero() } for i, v := range values { a.Index(index[i]).Set(v(f)) @@ -2947,7 +2949,7 @@ func _append(n *node) { if len(n.child) == 3 { c1, c2 := n.child[1], n.child[2] if (c1.typ.cat == valueT || c2.typ.cat == valueT) && c1.typ.rtype == c2.typ.rtype || - (c2.typ.cat == arrayT || c2.typ.cat == variadicT) && c2.typ.val.id() == n.typ.val.id() || + (c2.typ.cat == arrayT || c2.typ.cat == sliceT || c2.typ.cat == variadicT) && c2.typ.val.id() == n.typ.val.id() || isByteArray(c1.typ.TypeOf()) && isString(c2.typ.TypeOf()) { appendSlice(n) return diff --git a/interp/type.go b/interp/type.go index 37ce85445..5480e96b7 100644 --- a/interp/type.go +++ b/interp/type.go @@ -38,6 +38,7 @@ const ( int64T mapT ptrT + sliceT srcPkgT stringT structT @@ -75,6 +76,7 @@ var cats = [...]string{ int64T: "int64T", mapT: "mapT", ptrT: "ptrT", + sliceT: "sliceT", srcPkgT: "srcPkgT", stringT: "stringT", structT: "structT", @@ -109,19 +111,18 @@ type itype struct { cat tcat // Type category field []structField // Array of struct fields if structT or interfaceT key *itype // Type of key element if MapT or nil - val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT + val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT, sliceT or variadicT recv *itype // Receiver type for funcT or nil arg []*itype // Argument types if funcT or nil ret []*itype // Return types if funcT or nil method []*node // Associated methods or nil name string // name of type within its package for a defined type path string // for a defined type, the package import path - size int // Size of array if ArrayT + length int // length of array if ArrayT rtype reflect.Type // Reflection type if ValueT, or nil incomplete bool // true if type must be parsed again (out of order declarations) recursive bool // true if the type has an element which refer to itself untyped bool // true for a literal value (string or number) - sizedef bool // true if array size is computed from type definition isBinMethod bool // true if the type refers to a bin method function node *node // root AST node of type definition scope *scope // type declaration scope (in case of re-parse incomplete type) @@ -141,9 +142,6 @@ func errorMethodType(sc *scope) *itype { // nodeType returns a type definition for the corresponding AST subtree. func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { if n.typ != nil && !n.typ.incomplete { - if n.kind == sliceExpr { - n.typ.sizedef = false - } return n.typ, nil } @@ -169,10 +167,10 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { t.incomplete = t.val.incomplete case arrayType: - t.cat = arrayT c0 := n.child[0] if len(n.child) == 1 { // Array size is not defined. + t.cat = sliceT if t.val, err = nodeType(interp, sc, c0); err != nil { return nil, err } @@ -180,18 +178,19 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { break } // Array size is defined. + t.cat = arrayT switch v := c0.rval; { case v.IsValid(): // Size if defined by a constant litteral value. if isConstantValue(v.Type()) { c := v.Interface().(constant.Value) - t.size = constToInt(c) + t.length = constToInt(c) } else { - t.size = int(v.Int()) + t.length = int(v.Int()) } case c0.kind == ellipsisExpr: // [...]T expression, get size from the length of composite array. - t.size = arrayTypeLen(n.anc) + t.length = arrayTypeLen(n.anc) case c0.kind == identExpr: sym, _, ok := sc.lookup(c0.ident) if !ok { @@ -207,11 +206,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { break } if v, ok := sym.rval.Interface().(int); ok { - t.size = v + t.length = v break } if c, ok := sym.rval.Interface().(constant.Value); ok { - t.size = constToInt(c) + t.length = constToInt(c) break } t.incomplete = true @@ -225,12 +224,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { t.incomplete = true break } - t.size = constToInt(v) + t.length = constToInt(v) } if t.val, err = nodeType(interp, sc, n.child[1]); err != nil { return nil, err } - t.sizedef = true t.incomplete = t.incomplete || t.val.incomplete case basicLit: @@ -503,7 +501,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { break } switch lt.cat { - case arrayT, mapT: + case arrayT, mapT, sliceT, variadicT: t = lt.val } @@ -626,15 +624,14 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { case sliceExpr: t, err = nodeType(interp, sc, n.child[0]) + if err != nil { + return nil, err + } if t.cat == ptrT { t = t.val } - if err == nil && t.size != 0 { - t1 := *t - t1.size = 0 - t1.sizedef = false - t1.rtype = nil - t = &t1 + if t.cat == arrayT { + t = &itype{cat: sliceT, val: t.val, incomplete: t.incomplete, node: n, scope: sc} } case structType: @@ -786,7 +783,7 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool { } seen[t] = true switch t.cat { - case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT: + case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT: return t.val.referTo(name, seen) case funcT: for _, a := range t.arg { @@ -930,7 +927,7 @@ func isComplete(t *itype, visited map[string]bool) bool { return true } fallthrough - case arrayT, chanT, chanRecvT, chanSendT, ptrT: + case arrayT, chanT, chanRecvT, chanSendT, ptrT, sliceT, variadicT: return isComplete(t.val, visited) case funcT: complete := true @@ -1129,11 +1126,7 @@ func (t *itype) id() (res string) { case nilT: res = "nil" case arrayT: - if t.size == 0 { - res = "[]" + t.val.id() - } else { - res = "[" + strconv.Itoa(t.size) + "]" + t.val.id() - } + res = "[" + strconv.Itoa(t.length) + "]" + t.val.id() case chanT: res = "chan " + t.val.id() case chanSendT: @@ -1166,6 +1159,8 @@ func (t *itype) id() (res string) { res = "map[" + t.key.id() + "]" + t.val.id() case ptrT: res = "*" + t.val.id() + case sliceT: + res = "[]" + t.val.id() case structT: res = "struct{" for _, t := range t.field { @@ -1178,6 +1173,8 @@ func (t *itype) id() (res string) { res += t.rtype.PkgPath() + "." } res += t.rtype.Name() + case variadicT: + res = "..." + t.val.id() } return res } @@ -1191,7 +1188,7 @@ func (t *itype) zero() (v reflect.Value, err error) { case aliasT: v, err = t.val.zero() - case arrayT, ptrT, structT: + case arrayT, ptrT, structT, sliceT: v = reflect.New(t.frameType()).Elem() case valueT: @@ -1438,12 +1435,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T switch t.cat { case aliasT: t.rtype = t.val.refType(defined, wrapRecursive) - case arrayT, variadicT: - if t.sizedef { - t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined, wrapRecursive)) - } else { - t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive)) - } + case arrayT: + t.rtype = reflect.ArrayOf(t.length, t.val.refType(defined, wrapRecursive)) + case sliceT, variadicT: + t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive)) case chanT: t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive)) case chanRecvT: @@ -1518,12 +1513,10 @@ func (t *itype) frameType() (r reflect.Type) { switch t.cat { case aliasT: r = t.val.frameType() - case arrayT, variadicT: - if t.sizedef { - r = reflect.ArrayOf(t.size, t.val.frameType()) - } else { - r = reflect.SliceOf(t.val.frameType()) - } + case arrayT: + r = reflect.ArrayOf(t.length, t.val.frameType()) + case sliceT, variadicT: + r = reflect.SliceOf(t.val.frameType()) case funcT: r = reflect.TypeOf((*node)(nil)) case interfaceT: diff --git a/interp/typecheck.go b/interp/typecheck.go index 1fa7b0c76..72a73bb62 100644 --- a/interp/typecheck.go +++ b/interp/typecheck.go @@ -289,7 +289,10 @@ func (check typecheck) index(n *node, max int) error { } // arrayLitExpr type checks an array composite literal expression. -func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error { +func (check typecheck) arrayLitExpr(child []*node, typ *itype) error { + cat := typ.cat + length := typ.length + typ = typ.val visited := make(map[int]bool, len(child)) index := 0 for _, c := range child { @@ -301,7 +304,7 @@ func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error } n = c.child[1] index = int(vInt(c.child[0].rval)) - case length > 0 && index >= length: + case cat == arrayT && index >= length: return c.cfgErrorf("index %d is out of bounds (>= %d)", index, length) } @@ -904,7 +907,7 @@ func arrayDeref(typ *itype) *itype { return typ } - if typ.cat == ptrT && typ.val.cat == arrayT && typ.val.sizedef { + if typ.cat == ptrT && typ.val.cat == arrayT { return typ.val } return typ @@ -960,7 +963,7 @@ func (check typecheck) argument(p param, ftyp *itype, i, l int, ellipsis bool) e } t := p.Type().TypeOf() if t.Kind() != reflect.Slice || !(&itype{cat: valueT, rtype: t.Elem()}).assignableTo(atyp) { - return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: arrayT, val: atyp}).id()) + return p.nod.cfgErrorf("cannot use %s as type %s", p.nod.typ.id(), (&itype{cat: sliceT, val: atyp}).id()) } return nil }