From 772cd68feab2860bfa068ded55169ec8812afb81 Mon Sep 17 00:00:00 2001 From: Nicholas Wiersma Date: Tue, 31 Aug 2021 04:34:12 -0400 Subject: [PATCH] interp: make use of type constructors It was initially assumed that `nodeType` needed to rebuild the type from scratch, but this is not the case anymore. The existing type constructors are now used in `nodeType` to make it more readable. In doing this some bugs in type strings were found and fixed, along with adding the real package name to the type. As `ptrOf` is now the only place that pointer types are constructed, it is feasible to cache the pointer type on the value type. To ensures that we have a consistent pointer type for any value type. --- interp/gta.go | 10 ++++---- interp/interp.go | 2 +- interp/src.go | 4 +-- interp/type.go | 66 +++++++++++++++++++++--------------------------- 4 files changed, 37 insertions(+), 45 deletions(-) diff --git a/interp/gta.go b/interp/gta.go index e2753a84..e4265905 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -11,7 +11,7 @@ import ( // All function bodies are skipped. GTA is necessary to handle out of // order declarations and multiple source files packages. // rpath is the relative path to the directory containing the source for the package. -func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, error) { +func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([]*node, error) { sc := interp.initScopePkg(importPath) var err error var revisit []*node @@ -266,14 +266,14 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e switch n.child[1].kind { case identExpr, selectorExpr: - n.typ = namedOf(typ, importPath, typeName, withNode(n.child[0]), withScope(sc)) + n.typ = namedOf(typ, pkgName, typeName, withNode(n.child[0]), withScope(sc)) n.typ.incomplete = typ.incomplete n.typ.field = typ.field copy(n.typ.method, typ.method) default: n.typ = typ n.typ.name = typeName - n.typ.path = importPath + n.typ.path = pkgName } n.typ.str = n.typ.path + "." + n.typ.name @@ -312,11 +312,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e } // gtaRetry (re)applies gta until all global constants and types are defined. -func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error { +func (interp *Interpreter) gtaRetry(nodes []*node, importPath, pkgName string) error { revisit := []*node{} for { for _, n := range nodes { - list, err := interp.gta(n, importPath, importPath) + list, err := interp.gta(n, importPath, importPath, pkgName) if err != nil { return err } diff --git a/interp/interp.go b/interp/interp.go index b88bc95e..23381815 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -537,7 +537,7 @@ func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, } // Perform global types analysis. - if err = interp.gtaRetry([]*node{root}, pkgName); err != nil { + if err = interp.gtaRetry([]*node{root}, pkgName, pkgName); err != nil { return res, err } diff --git a/interp/src.go b/interp/src.go index 2d2c00a5..758e1dd5 100644 --- a/interp/src.go +++ b/interp/src.go @@ -97,7 +97,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s subRPath := effectivePkg(rPath, importPath) var list []*node - list, err = interp.gta(root, subRPath, importPath) + list, err = interp.gta(root, subRPath, importPath, pkgName) if err != nil { return "", err } @@ -106,7 +106,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s // Revisit incomplete nodes where GTA could not complete. for _, nodes := range revisit { - if err = interp.gtaRetry(nodes, importPath); err != nil { + if err = interp.gtaRetry(nodes, importPath, pkgName); err != nil { return "", err } } diff --git a/interp/type.go b/interp/type.go index a748d6a1..9606755a 100644 --- a/interp/type.go +++ b/interp/type.go @@ -118,6 +118,7 @@ type itype struct { recv *itype // Receiver type for funcT or nil arg []*itype // Argument types if funcT or nil ret []*itype // Return types if funcT or nil + ptr *itype // Pointer to this type. Might be 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 @@ -215,10 +216,14 @@ func wrapperValueTOf(rtype reflect.Type, val *itype, opts ...itypeOption) *itype // ptrOf returns a pointer to t. func ptrOf(val *itype, opts ...itypeOption) *itype { + if val.ptr != nil { + return val.ptr + } t := &itype{cat: ptrT, val: val, str: "*" + val.str} for _, opt := range opts { opt(t) } + val.ptr = t return t } @@ -246,16 +251,16 @@ const ( // chanOf returns a channel of the underlying type val. func chanOf(val *itype, dir chanDir, opts ...itypeOption) *itype { cat := chanT - str := "chan" + str := "chan " switch dir { case chanSend: cat = chanSendT - str = "chan<-" + str = "chan<- " case chanRecv: cat = chanRecvT - str = "<-chan" + str = "<-chan " } - t := &itype{cat: cat, val: val, str: str} + t := &itype{cat: cat, val: val, str: str + val.str} for _, opt := range opts { opt(t) } @@ -298,25 +303,22 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { var err error switch n.kind { case addressExpr, starExpr: - t.cat = ptrT - if t.val, err = nodeType(interp, sc, n.child[0]); err != nil { + val, err := nodeType(interp, sc, n.child[0]) + if err != nil { return nil, err } - repr.WriteString("*" + t.val.str) - t.incomplete = t.val.incomplete + t = ptrOf(val, withNode(n), withScope(sc)) + t.incomplete = val.incomplete case arrayType: 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 { + val, err := nodeType(interp, sc, c0) + if err != nil { return nil, err } - t.incomplete = t.val.incomplete - if t.val != nil { - repr.WriteString("[]" + t.val.str) - } + t = sliceOf(val, withNode(n), withScope(sc)) + t.incomplete = val.incomplete break } // Array size is defined. @@ -526,29 +528,20 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { case compositeLitExpr: t, err = nodeType(interp, sc, n.child[0]) - case chanType: - t.cat = chanT - if t.val, err = nodeType(interp, sc, n.child[0]); err != nil { + case chanType, chanTypeRecv, chanTypeSend: + dir := chanSendRecv + switch n.kind { + case chanTypeRecv: + dir = chanRecv + case chanTypeSend: + dir = chanSend + } + val, err := nodeType(interp, sc, n.child[0]) + if err != nil { return nil, err } - repr.WriteString("chan " + t.val.str) - t.incomplete = t.val.incomplete - - case chanTypeRecv: - t.cat = chanRecvT - if t.val, err = nodeType(interp, sc, n.child[0]); err != nil { - return nil, err - } - repr.WriteString("<-chan " + t.val.str) - t.incomplete = t.val.incomplete - - case chanTypeSend: - t.cat = chanSendT - if t.val, err = nodeType(interp, sc, n.child[0]); err != nil { - return nil, err - } - repr.WriteString("chan<- " + t.val.str) - t.incomplete = t.val.incomplete + t = chanOf(val, dir, withNode(n), withScope(sc)) + t.incomplete = val.incomplete case ellipsisExpr: t.cat = variadicT @@ -690,7 +683,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { return nil, err } t.field = append(t.field, structField{name: f0.ident, typ: typ}) - repr.WriteString(" " + f0.ident + typ.str[4:]) incomplete = incomplete || typ.incomplete } methStr := methodsTypeString(t.field)