fix: improve handling of out of order declarations (#344)

This commit is contained in:
Marc Vertes
2019-09-05 11:37:35 +02:00
committed by Ludovic Fernandez
parent d23a7e1d8b
commit b0937618b0
11 changed files with 201 additions and 74 deletions

21
_test/fun6.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"fmt"
"sync"
)
func NewPool() Pool { return Pool{} }
type Pool struct {
p *sync.Pool
}
var _pool = NewPool()
func main() {
fmt.Println(_pool)
}
// Output:
// {<nil>}

33
_test/method25.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"sync"
)
func (p Pool) Get() *Buffer { return &Buffer{} }
func NewPool() Pool { return Pool{} }
type Buffer struct {
bs []byte
pool Pool
}
type Pool struct {
p *sync.Pool
}
var (
_pool = NewPool()
Get = _pool.Get
)
func main() {
fmt.Println(_pool)
fmt.Println(Get())
}
// Output:
// {<nil>}
// &{[] {<nil>}}

18
_test/method26.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func NewT(name string) *T { return &T{name} }
var C = NewT("test")
func (t *T) f() { println(t == C) }
type T struct {
Name string
}
func main() {
C.f()
}
// Output:
// true

20
_test/struct26.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
func newT2() *T2 { return &T2{} }
type T2 struct {
T1
}
type T1 struct {
bs []byte
}
func main() {
fmt.Println(newT2())
}
// Output:
// &{{[]}}

18
_test/struct27.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
func (f *Foo) Boo() { fmt.Println(f.name, "Boo") }
type Foo struct {
name string
fun func(f *Foo)
}
func main() {
t := &Foo{name: "foo"}
t.Boo()
}
// Output:
// foo Boo

19
_test/struct28.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "fmt"
type T1 struct {
T2
}
type T2 struct {
*T1
}
func main() {
t := T1{}
fmt.Println(t)
}
// Output:
// {{<nil>}}

View File

@@ -355,6 +355,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if atyp != nil {
dest.typ = atyp
} else {
if src.typ, err = nodeType(interp, sc, src); err != nil {
return
}
dest.typ = src.typ
}
if dest.typ.sizedef {

View File

@@ -79,41 +79,36 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
return false
}
if !isMethod(n) {
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if len(n.child[0].child) > 0 {
// function is a method, add it to the related type
if isMethod(n) {
// Add a method symbol in the receiver type name space
var rcvrtype *itype
var typeName string
n.ident = n.child[1].ident
rcvr := n.child[0].child[0]
if len(rcvr.child) < 2 {
// Receiver var name is skipped in method declaration (fix that in AST ?)
typeName = rcvr.child[0].ident
} else {
typeName = rcvr.child[1].ident
}
rtn := rcvr.lastChild()
typeName := rtn.ident
if typeName == "" {
// The receiver is a pointer, retrieve typeName from indirection
typeName = rcvr.lastChild().child[0].ident
typeName = rtn.child[0].ident
elementType := sc.getType(typeName)
if elementType == nil {
// Add type if necessary, so method can be registered
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath, incomplete: true, node: rtn.child[0], scope: sc}}
elementType = sc.sym[typeName].typ
}
rcvrtype = &itype{cat: ptrT, val: elementType}
rcvrtype = &itype{cat: ptrT, val: elementType, incomplete: elementType.incomplete, node: rtn, scope: sc}
elementType.method = append(elementType.method, n)
} else {
rcvrtype = sc.getType(typeName)
if rcvrtype == nil {
// Add type if necessary, so method can be registered
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}}
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath, incomplete: true, node: rtn, scope: sc}}
rcvrtype = sc.sym[typeName].typ
}
}
rcvrtype.method = append(rcvrtype.method, n)
} else {
// Add a function symbol in the package name space
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
return false
@@ -165,6 +160,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
}
sc.sym[typeName].typ = n.typ
if n.typ.incomplete {
revisit = append(revisit, n)
}
return false
}
return true

View File

@@ -198,12 +198,12 @@ func convert(n *node) {
}
}
func isRecursiveStruct(t *itype) bool {
if t.cat == structT && t.rtype.Kind() == reflect.Interface {
func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
if t.cat == structT && rtype.Kind() == reflect.Interface {
return true
}
if t.cat == ptrT {
return isRecursiveStruct(t.val)
return isRecursiveStruct(t.val, t.rtype.Elem())
}
return false
}
@@ -230,7 +230,7 @@ func assign(n *node) {
case src.kind == basicLit && src.val == nil:
t := dest.typ.TypeOf()
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
case isRecursiveStruct(dest.typ):
case isRecursiveStruct(dest.typ, dest.typ.rtype):
svalue[i] = genValueInterfacePtr(src)
default:
svalue[i] = genValue(src)
@@ -1087,7 +1087,7 @@ func getPtrIndexSeq(n *node) {
index := n.val.([]int)
tnext := getExec(n.tnext)
var value func(*frame) reflect.Value
if isRecursiveStruct(n.child[0].typ) {
if isRecursiveStruct(n.child[0].typ, n.child[0].typ.rtype) {
v := genValue(n.child[0])
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
} else {
@@ -1727,7 +1727,7 @@ func _append(n *node) {
values := make([]func(*frame) reflect.Value, l)
for i, arg := range args {
switch {
case isRecursiveStruct(n.typ.val):
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
values[i] = genValueInterfacePtr(arg)
case arg.typ.untyped:
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
@@ -1747,7 +1747,7 @@ func _append(n *node) {
} else {
var value0 func(*frame) reflect.Value
switch {
case isRecursiveStruct(n.typ.val):
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
value0 = genValueInterfacePtr(n.child[2])
case n.child[2].typ.untyped:
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())

View File

@@ -44,17 +44,16 @@ func (k sKind) String() string {
// A symbol represents an interpreter object such as type, constant, var, func,
// label, builtin or binary object. Symbols are defined within a scope.
type symbol struct {
kind sKind
typ *itype // Type of value
node *node // Node value if index is negative
from []*node // list of nodes jumping to node if kind is label, or nil
recv *receiver // receiver node value, if sym refers to a method
index int // index of value in frame or -1
rval reflect.Value // default value (used for constants)
path string // package path if typ.cat is SrcPkgT or BinPkgT
builtin bltnGenerator // Builtin function or nil
global bool // true if symbol is defined in global space
recursive bool // true if symbol is a recursive type definition
kind sKind
typ *itype // Type of value
node *node // Node value if index is negative
from []*node // list of nodes jumping to node if kind is label, or nil
recv *receiver // receiver node value, if sym refers to a method
index int // index of value in frame or -1
rval reflect.Value // default value (used for constants)
path string // package path if typ.cat is SrcPkgT or BinPkgT
builtin bltnGenerator // Builtin function or nil
global bool // true if symbol is defined in global space
// TODO: implement constant checking
//constant bool // true if symbol value is constant
}

View File

@@ -130,6 +130,16 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
var t = &itype{node: n, scope: sc}
if n.anc.kind == typeSpec {
name := n.anc.child[0].ident
if sym := sc.sym[name]; sym != nil {
// recover previously declared methods
t.method = sym.typ.method
t.pkgPath = sym.typ.pkgPath
t.name = name
}
}
switch n.kind {
case addressExpr, starExpr:
t.cat = ptrT
@@ -314,11 +324,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case identExpr:
if sym, _, found := sc.lookup(n.ident); found {
t = sym.typ
if sym.recursive && t.incomplete {
t.incomplete = false
t.rtype = reflect.TypeOf((*interface{})(nil)).Elem()
sym.typ = t
}
if t.incomplete && t.node != n {
m := t.method
if t, err = nodeType(interp, sc, t.node); err != nil {
@@ -416,7 +421,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
var sym *symbol
if sname := structName(n); sname != "" {
if sym, _, found = sc.lookup(sname); found && sym.kind == typeSym {
sym.recursive = true
sym.typ = t
}
}
for _, c := range n.child[0].child {
@@ -460,7 +465,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
if t.cat == nilT && !t.incomplete {
err = n.cfgErrorf("use of untyped nil")
err = n.cfgErrorf("use of untyped nil %s", t.name)
}
return t, err
@@ -517,15 +522,23 @@ func init() {
func (t *itype) finalize() (*itype, error) {
var err cfgError
if t.incomplete {
sym, _, found := t.scope.lookup(t.name)
if found && !sym.typ.incomplete {
sym.typ.method = append(sym.typ.method, t.method...)
return sym.typ, nil
}
m := t.method
if t, err = nodeType(t.node.interp, t.scope, t.node); err != nil {
return nil, err
}
if t.incomplete {
return nil, t.node.cfgErrorf("incomplete type")
return nil, t.node.cfgErrorf("incomplete type %s", t.name)
}
t.method = m
t.node.typ = t
if sym != nil {
sym.typ = t
}
}
return t, err
}
@@ -691,7 +704,7 @@ func exportName(s string) string {
}
// TypeOf returns the reflection type of dynamic interpreter type t.
func (t *itype) TypeOf() reflect.Type {
func (t *itype) Type(name string) reflect.Type {
if t.rtype != nil {
return t.rtype
}
@@ -702,26 +715,29 @@ func (t *itype) TypeOf() reflect.Type {
panic(err)
}
}
if name == "" {
name = t.name
}
switch t.cat {
case arrayT:
if t.size > 0 {
t.rtype = reflect.ArrayOf(t.size, t.val.TypeOf())
t.rtype = reflect.ArrayOf(t.size, t.val.Type(name))
} else {
t.rtype = reflect.SliceOf(t.val.TypeOf())
t.rtype = reflect.SliceOf(t.val.Type(name))
}
case chanT:
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.TypeOf())
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.Type(name))
case errorT:
t.rtype = reflect.TypeOf(new(error)).Elem()
case funcT:
in := make([]reflect.Type, len(t.arg))
out := make([]reflect.Type, len(t.ret))
for i, v := range t.arg {
in[i] = v.TypeOf()
in[i] = v.Type(name)
}
for i, v := range t.ret {
out[i] = v.TypeOf()
out[i] = v.Type(name)
}
t.rtype = reflect.FuncOf(in, out, false)
case interfaceT:
@@ -729,18 +745,14 @@ func (t *itype) TypeOf() reflect.Type {
case mapT:
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
case ptrT:
// FIXME: the following should probably be done in nodeType() instead
if t.val.cat == nilT {
t.val.incomplete = true
if s, _, ok := t.scope.lookup(t.val.name); ok {
t.val = s.typ
}
if t.val != nil && name != "" && t.val.name == name {
t.val.rtype = reflect.TypeOf(new(interface{})).Elem()
}
t.rtype = reflect.PtrTo(t.val.TypeOf())
t.rtype = reflect.PtrTo(t.val.Type(name))
case structT:
var fields []reflect.StructField
for _, f := range t.field {
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.TypeOf(), Tag: reflect.StructTag(f.tag)}
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.Type(name), Tag: reflect.StructTag(f.tag)}
fields = append(fields, field)
}
t.rtype = reflect.StructOf(fields)
@@ -752,6 +764,10 @@ func (t *itype) TypeOf() reflect.Type {
return t.rtype
}
func (t *itype) TypeOf() reflect.Type {
return t.Type(t.name)
}
func (t *itype) frameType() (r reflect.Type) {
var err error
if t, err = t.finalize(); err != nil {
@@ -764,29 +780,11 @@ func (t *itype) frameType() (r reflect.Type) {
} else {
r = reflect.SliceOf(t.val.frameType())
}
//case ChanT:
// r = reflect.ChanOf(reflect.BothDir, t.val.frameType())
//case ErrorT:
// r = reflect.TypeOf(new(error)).Elem()
case funcT:
r = reflect.TypeOf((*node)(nil))
case interfaceT:
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
//case MapT:
// r = reflect.MapOf(t.key.frameType(), t.val.frameType())
//case PtrT:
// r = reflect.PtrTo(t.val.frameType())
//case StructT:
// var fields []reflect.StructField
// for _, f := range t.field {
// field := reflect.StructField{Name: exportName(f.name), Type: f.typ.frameType()}
// fields = append(fields, field)
// }
// r = reflect.StructOf(fields)
default:
// if z, _ := t.zero(); z.IsValid() {
// r = z.Type()
// }
r = t.TypeOf()
}
return r