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:
25
_test/issue-1285.go
Normal file
25
_test/issue-1285.go
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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...)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user