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:
Marc Vertes
2021-01-14 15:46:04 +01:00
committed by GitHub
parent 5cd1e11379
commit 8a1f9ef44e
4 changed files with 120 additions and 4 deletions

17
_test/interface48.go Normal file
View 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
View 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

View File

@@ -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
}

View File

@@ -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