diff --git a/_test/interface48.go b/_test/interface48.go new file mode 100644 index 00000000..302bed41 --- /dev/null +++ b/_test/interface48.go @@ -0,0 +1,17 @@ +package main + +import "fmt" + +type I1 interface{ A } + +type A = I2 + +type I2 interface{ F() I1 } + +func main() { + var i I1 + fmt.Println(i) +} + +// Output: +// diff --git a/_test/interface49.go b/_test/interface49.go new file mode 100644 index 00000000..b9fb9fc9 --- /dev/null +++ b/_test/interface49.go @@ -0,0 +1,45 @@ +package main + +type Descriptor interface { + ParentFile() FileDescriptor +} + +type FileDescriptor interface { + Enums() EnumDescriptors + Services() ServiceDescriptors +} + +type EnumDescriptors interface { + Get(i int) EnumDescriptor +} + +type EnumDescriptor interface { + Values() EnumValueDescriptors +} + +type EnumValueDescriptors interface { + Get(i int) EnumValueDescriptor +} + +type EnumValueDescriptor interface { + Descriptor +} + +type ServiceDescriptors interface { + Get(i int) ServiceDescriptor +} + +type ServiceDescriptor interface { + Descriptor + isServiceDescriptor +} + +type isServiceDescriptor interface{ ProtoType(ServiceDescriptor) } + +func main() { + var d Descriptor + println(d == nil) +} + +// Output: +// true diff --git a/interp/gta.go b/interp/gta.go index 71c602b4..19cef809 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -319,7 +319,50 @@ func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error { } if len(revisit) > 0 { - return revisit[0].cfgErrorf("constant definition loop") + n := revisit[0] + if n.kind == typeSpec { + if err := definedType(n.typ); err != nil { + return err + } + } + return n.cfgErrorf("constant definition loop") + } + return nil +} + +func definedType(typ *itype) error { + if !typ.incomplete { + return nil + } + switch typ.cat { + case interfaceT, structT: + for _, f := range typ.field { + if err := definedType(f.typ); err != nil { + return err + } + } + case funcT: + for _, t := range typ.arg { + if err := definedType(t); err != nil { + return err + } + } + for _, t := range typ.ret { + if err := definedType(t); err != nil { + return err + } + } + case mapT: + if err := definedType(typ.key); err != nil { + return err + } + fallthrough + case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT: + if err := definedType(typ.val); err != nil { + return err + } + case nilT: + return typ.node.cfgErrorf("undefined: %s", typ.node.ident) } return nil } diff --git a/interp/type.go b/interp/type.go index d470e97b..b92ef6ba 100644 --- a/interp/type.go +++ b/interp/type.go @@ -109,7 +109,7 @@ 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 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 @@ -449,6 +449,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { } } t = sym.typ + if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT { + t.incomplete = false + } if t.incomplete && t.node != n { m := t.method if t, err = nodeType(interp, sc, t.node); err != nil { @@ -879,13 +882,19 @@ func isComplete(t *itype, visited map[string]bool) bool { } name := t.path + "/" + t.name if visited[name] { - return !t.incomplete + return true } if t.name != "" { visited[name] = true } switch t.cat { - case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT: + case aliasT: + if t.val != nil && t.val.cat != nilT { + // A type aliased to a partially defined type is considered complete, to allow recursivity. + return true + } + fallthrough + case arrayT, chanT, chanRecvT, chanSendT, ptrT: return isComplete(t.val, visited) case funcT: complete := true @@ -899,6 +908,8 @@ func isComplete(t *itype, visited map[string]bool) bool { case interfaceT, structT: complete := true for _, f := range t.field { + // Field implicit type names must be marked as visited, to break false circles. + visited[f.typ.path+"/"+f.typ.name] = true complete = complete && isComplete(f.typ, visited) } return complete