interp: parse circular interface definitions
An undefined type detection function has been added to better diagnose incomplete type definitions. Implicit type names in interface or struct declarations are now better handled. The incomplete status is not fowarded to aliased type declarations to handle circular definitions. Fixes #999 and #995. Improves #260 (goes farther, but still fails).
This commit is contained in:
17
_test/interface48.go
Normal file
17
_test/interface48.go
Normal file
@@ -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:
|
||||
// <nil>
|
||||
45
_test/interface49.go
Normal file
45
_test/interface49.go
Normal file
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user