interp: fix use of builtins in type definitions

Make len() and cap() work on pointers. Preserve scope in case of
nested calls of cfg.

Fixes #1285.
This commit is contained in:
Marc Vertes
2021-10-18 10:50:14 +02:00
committed by GitHub
parent aa7f0849e3
commit 229ddfdae1
7 changed files with 66 additions and 17 deletions

25
_test/issue-1285.go Normal file
View File

@@ -0,0 +1,25 @@
package main
type (
T1 struct{ Path [12]int8 }
T2 struct{ Path *[12]int8 }
)
var (
t11 = &T1{}
t21 = &T2{}
)
func main() {
b := [12]byte{}
t12 := &T1{}
t22 := &T2{}
b11 := (*[len(t11.Path)]byte)(&b)
b12 := (*[len(t12.Path)]byte)(&b)
b21 := (*[len(t21.Path)]byte)(&b)
b22 := (*[len(t22.Path)]byte)(&b)
println(len(b11), len(b12), len(b21), len(b22))
}
// Output:
// 12 12 12 12

View File

@@ -49,8 +49,10 @@ const nilIdent = "nil"
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run.
func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node, error) {
sc := interp.initScopePkg(importPath, pkgName)
func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string) ([]*node, error) {
if sc == nil {
sc = interp.initScopePkg(importPath, pkgName)
}
check := typecheck{scope: sc}
var initNodes []*node
var err error
@@ -443,7 +445,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
// values which may be used in further declarations.
if !sc.global {
for _, c := range n.child {
if _, err = interp.cfg(c, importPath, pkgName); err != nil {
if _, err = interp.cfg(c, sc, importPath, pkgName); err != nil {
// No error processing here, to allow recovery in subtree nodes.
err = nil
}
@@ -887,7 +889,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
// Store result directly to frame output location, to avoid a frame copy.
n.findex = 0
case bname == "cap" && isInConstOrTypeDecl(n):
switch n.child[1].typ.TypeOf().Kind() {
t := n.child[1].typ.TypeOf()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Array, reflect.Chan:
capConst(n)
default:
@@ -896,7 +902,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
n.findex = notInFrame
n.gen = nop
case bname == "len" && isInConstOrTypeDecl(n):
switch n.child[1].typ.TypeOf().Kind() {
t := n.child[1].typ.TypeOf()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Array, reflect.Chan, reflect.String:
lenConst(n)
default:
@@ -1257,7 +1267,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
// retry with the filename, in case ident is a package name.
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
if !found {
err = n.cfgErrorf("undefined: %s", n.ident)
err = n.cfgErrorf("undefined: %s %d", n.ident, n.index)
break
}
}
@@ -2409,7 +2419,7 @@ func isInConstOrTypeDecl(n *node) bool {
anc := n.anc
for anc != nil {
switch anc.kind {
case constDecl, typeDecl:
case constDecl, typeDecl, arrayType, chanType:
return true
case varDecl, funcDecl:
return false

View File

@@ -25,7 +25,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
case constDecl:
// Early parse of constDecl subtree, to compute all constant
// values which may be used in further declarations.
if _, err = interp.cfg(n, importPath, pkgName); err != nil {
if _, err = interp.cfg(n, sc, importPath, pkgName); err != nil {
// No error processing here, to allow recovery in subtree nodes.
// TODO(marc): check for a non recoverable error and return it for better diagnostic.
err = nil
@@ -61,7 +61,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
dest, src := n.child[i], n.child[sbase+i]
val := src.rval
if n.anc.kind == constDecl {
if _, err2 := interp.cfg(n, importPath, pkgName); err2 != nil {
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
// Constant value can not be computed yet.
// Come back when child dependencies are known.
revisit = append(revisit, n)

View File

@@ -79,7 +79,7 @@ func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
}
// Annotate AST with CFG informations.
initNodes, err := interp.cfg(root, pkgName, pkgName)
initNodes, err := interp.cfg(root, nil, pkgName, pkgName)
if err != nil {
if interp.cfgDot {
dotCmd := interp.dotCmd

View File

@@ -3144,23 +3144,37 @@ func _delete(n *node) {
}
func capConst(n *node) {
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
// There is no Cap() method for reflect.Type, just return Len() instead.
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
lenConst(n)
}
func lenConst(n *node) {
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
if c1 := n.child[1]; c1.rval.IsValid() {
c1 := n.child[1]
if c1.rval.IsValid() {
n.rval.SetInt(int64(len(vString(c1.rval))))
} else {
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
return
}
t := c1.typ.TypeOf()
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
n.rval.SetInt(int64(t.Len()))
}
func _len(n *node) {
dest := genValueOutput(n, reflect.TypeOf(int(0)))
value := genValue(n.child[1])
if isPtr(n.child[1].typ) {
val := value
value = func(f *frame) reflect.Value {
v := val(f).Elem()
for v.Type().Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
}
next := getExec(n.tnext)
if wantEmptyInterface(n) {

View File

@@ -122,7 +122,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
// Generate control flow graphs.
for _, root := range rootNodes {
var nodes []*node
if nodes, err = interp.cfg(root, importPath, pkgName); err != nil {
if nodes, err = interp.cfg(root, nil, importPath, pkgName); err != nil {
return "", err
}
initNodes = append(initNodes, nodes...)

View File

@@ -456,7 +456,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
length = int(vInt(sym.rval))
default:
// Size is defined by a numeric constant expression.
if _, err = interp.cfg(c0, sc.pkgID, sc.pkgName); err != nil {
if _, err = interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
return nil, err
}
v, ok := c0.rval.Interface().(constant.Value)