mirror of
https://github.com/golang/go
synced 2024-11-18 22:55:23 -07:00
go.tools/go/types: fix type cycle tracking
Fixes golang/go#7236. LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/58430043
This commit is contained in:
parent
f97ec06d2c
commit
9cd9226d63
@ -357,7 +357,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
// make(T, n, m)
|
||||
// (no argument evaluated yet)
|
||||
arg0 := call.Args[0]
|
||||
T := check.typ(arg0, nil, false)
|
||||
T := check.typ(arg0, nil, nil)
|
||||
if T == Typ[Invalid] {
|
||||
return
|
||||
}
|
||||
@ -395,7 +395,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
case _New:
|
||||
// new(T)
|
||||
// (no argument evaluated yet)
|
||||
T := check.typ(call.Args[0], nil, false)
|
||||
T := check.typ(call.Args[0], nil, nil)
|
||||
if T == Typ[Invalid] {
|
||||
return
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) {
|
||||
|
||||
// objDecl type-checks the declaration of obj in its respective file scope.
|
||||
// See typeDecl for the details on def and cycleOk.
|
||||
func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) {
|
||||
func (check *checker) objDecl(obj Object, def *Named, cycle []*TypeName) {
|
||||
d := check.objMap[obj]
|
||||
|
||||
// adjust file scope for current object
|
||||
@ -55,7 +55,7 @@ func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) {
|
||||
check.decl = d // new package-level var decl
|
||||
check.varDecl(obj, d.lhs, d.typ, d.init)
|
||||
case *TypeName:
|
||||
check.typeDecl(obj, d.typ, def, cycleOk)
|
||||
check.typeDecl(obj, d.typ, def, cycle)
|
||||
case *Func:
|
||||
check.funcDecl(obj, d)
|
||||
default:
|
||||
@ -68,6 +68,8 @@ func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) {
|
||||
}
|
||||
|
||||
func (check *checker) constDecl(obj *Const, typ, init ast.Expr) {
|
||||
// TODO(gri) consider using the same cycle detection as for types
|
||||
// so that we can print the actual cycle in case of an error
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
@ -81,7 +83,7 @@ func (check *checker) constDecl(obj *Const, typ, init ast.Expr) {
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
t := check.typ(typ, nil, false)
|
||||
t := check.typ(typ, nil, nil)
|
||||
if !isConstType(t) {
|
||||
check.errorf(typ.Pos(), "invalid constant type %s", t)
|
||||
obj.typ = Typ[Invalid]
|
||||
@ -103,6 +105,8 @@ func (check *checker) constDecl(obj *Const, typ, init ast.Expr) {
|
||||
|
||||
// TODO(gri) document arguments
|
||||
func (check *checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
|
||||
// TODO(gri) consider using the same cycle detection as for types
|
||||
// so that we can print the actual cycle in case of an error
|
||||
if obj.visited {
|
||||
check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name)
|
||||
obj.typ = Typ[Invalid]
|
||||
@ -115,7 +119,7 @@ func (check *checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
|
||||
|
||||
// determine type, if any
|
||||
if typ != nil {
|
||||
obj.typ = check.typ(typ, nil, false)
|
||||
obj.typ = check.typ(typ, nil, nil)
|
||||
}
|
||||
|
||||
// check initialization
|
||||
@ -171,7 +175,7 @@ func (n *Named) setUnderlying(typ Type) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) {
|
||||
func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycle []*TypeName) {
|
||||
assert(obj.Type() == nil)
|
||||
|
||||
// type declarations cannot use iota
|
||||
@ -182,7 +186,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk
|
||||
obj.typ = named // make sure recursive type declarations terminate
|
||||
|
||||
// determine underlying type of named
|
||||
check.typ(typ, named, cycleOk)
|
||||
check.typ(typ, named, append(cycle, obj))
|
||||
|
||||
// The underlying type of named may be itself a named type that is
|
||||
// incomplete:
|
||||
@ -199,9 +203,6 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk
|
||||
// any forward chain (they always end in an unnamed type).
|
||||
named.underlying = underlying(named.underlying)
|
||||
|
||||
// the underlying type has been determined
|
||||
named.complete = true
|
||||
|
||||
// type-check signatures of associated methods
|
||||
methods := check.methods[obj.name]
|
||||
if len(methods) == 0 {
|
||||
@ -242,7 +243,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk
|
||||
}
|
||||
}
|
||||
check.recordObject(check.objMap[m].fdecl.Name, m)
|
||||
check.objDecl(m, nil, true)
|
||||
check.objDecl(m, nil, nil)
|
||||
// Methods with blank _ names cannot be found.
|
||||
// Don't add them to the method list.
|
||||
if m.name != "_" {
|
||||
@ -363,7 +364,7 @@ func (check *checker) declStmt(decl ast.Decl) {
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
check.declare(check.topScope, s.Name, obj)
|
||||
check.typeDecl(obj, s.Type, nil, false)
|
||||
check.typeDecl(obj, s.Type, nil, nil)
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||
|
@ -943,7 +943,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||
goto Error // error was reported before
|
||||
|
||||
case *ast.Ident:
|
||||
check.ident(x, e, nil, false)
|
||||
check.ident(x, e, nil, nil)
|
||||
|
||||
case *ast.Ellipsis:
|
||||
// ellipses are handled explicitly where they are legal
|
||||
@ -959,7 +959,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
if sig, ok := check.typ(e.Type, nil, false).(*Signature); ok {
|
||||
if sig, ok := check.typ(e.Type, nil, nil).(*Signature); ok {
|
||||
// Anonymous functions are considered part of the
|
||||
// init expression/func declaration which contains
|
||||
// them: use existing package-level declaration info.
|
||||
@ -983,12 +983,12 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||
// We have an "open" [...]T array type.
|
||||
// Create a new ArrayType with unknown length (-1)
|
||||
// and finish setting it up after analyzing the literal.
|
||||
typ = &Array{len: -1, elem: check.typ(atyp.Elt, nil, false)}
|
||||
typ = &Array{len: -1, elem: check.typ(atyp.Elt, nil, nil)}
|
||||
openArray = true
|
||||
}
|
||||
}
|
||||
if typ == nil {
|
||||
typ = check.typ(e.Type, nil, false)
|
||||
typ = check.typ(e.Type, nil, nil)
|
||||
}
|
||||
}
|
||||
if typ == nil {
|
||||
@ -1309,7 +1309,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||
check.invalidAST(e.Pos(), "use of .(type) outside type switch")
|
||||
goto Error
|
||||
}
|
||||
T := check.typ(e.Type, nil, false)
|
||||
T := check.typ(e.Type, nil, nil)
|
||||
if T == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
@ -1365,7 +1365,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
|
||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||
x.mode = typexpr
|
||||
x.typ = check.typ(e, nil, false)
|
||||
x.typ = check.typ(e, nil, nil)
|
||||
// Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
|
||||
// even though check.typ has already called it. This is fine as both
|
||||
// times the same expression and type are recorded. It is also not a
|
||||
|
@ -354,13 +354,18 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
||||
|
||||
// Phase 3: Typecheck all objects in objMap, but not function bodies.
|
||||
|
||||
check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet)
|
||||
check.initMap = initMap
|
||||
check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet)
|
||||
emptyCycle := make([]*TypeName, 0, 8) // re-use the same underlying array for cycle detection
|
||||
for _, obj := range objectsOf(check.objMap) {
|
||||
if obj.Type() == nil {
|
||||
check.objDecl(obj, nil, false)
|
||||
if trace {
|
||||
check.trace(obj.Pos(), "-- resolving %s", obj.Name())
|
||||
}
|
||||
check.objDecl(obj, nil, emptyCycle)
|
||||
}
|
||||
}
|
||||
emptyCycle = nil // not needed anymore
|
||||
check.objMap = nil // not needed anymore
|
||||
|
||||
// At this point we may have a non-empty check.methods map; this means that not all
|
||||
|
60
go/types/testdata/cycles.src
vendored
60
go/types/testdata/cycles.src
vendored
@ -6,10 +6,10 @@ package cycles
|
||||
|
||||
type (
|
||||
T0 int
|
||||
T1 /* ERROR "cycle" */ T1
|
||||
T1 /* ERROR cycle */ T1
|
||||
T2 *T2
|
||||
|
||||
T3 /* ERROR "cycle" */ T4
|
||||
T3 /* ERROR cycle */ T4
|
||||
T4 T5
|
||||
T5 T3
|
||||
|
||||
@ -18,10 +18,10 @@ type (
|
||||
T8 T6
|
||||
|
||||
// arrays
|
||||
A0 /* ERROR "cycle" */ [10]A0
|
||||
A0 /* ERROR cycle */ [10]A0
|
||||
A1 [10]*A1
|
||||
|
||||
A2 /* ERROR "cycle" */ [10]A3
|
||||
A2 /* ERROR cycle */ [10]A3
|
||||
A3 [10]A4
|
||||
A4 A2
|
||||
|
||||
@ -32,12 +32,12 @@ type (
|
||||
L0 []L0
|
||||
|
||||
// structs
|
||||
S0 /* ERROR "cycle" */ struct{ _ S0 }
|
||||
S1 /* ERROR "cycle" */ struct{ S1 }
|
||||
S0 /* ERROR cycle */ struct{ _ S0 }
|
||||
S1 /* ERROR cycle */ struct{ S1 }
|
||||
S2 struct{ _ *S2 }
|
||||
S3 struct{ *S3 }
|
||||
|
||||
S4 /* ERROR "cycle" */ struct{ S5 }
|
||||
S4 /* ERROR cycle */ struct{ S5 }
|
||||
S5 struct{ S6 }
|
||||
S6 S4
|
||||
|
||||
@ -50,9 +50,9 @@ type (
|
||||
F2 func(F2) F2
|
||||
|
||||
// interfaces
|
||||
I0 /* ERROR "cycle" */ interface{ I0 }
|
||||
I0 /* ERROR cycle */ interface{ I0 }
|
||||
|
||||
I1 /* ERROR "cycle" */ interface{ I2 }
|
||||
I1 /* ERROR cycle */ interface{ I2 }
|
||||
I2 interface{ I3 }
|
||||
I3 interface{ I1 }
|
||||
|
||||
@ -63,7 +63,7 @@ type (
|
||||
I6 interface{ I5 }
|
||||
|
||||
// maps
|
||||
M0 map[M0 /* ERROR "invalid map key" */ ]M0
|
||||
M0 map[M0 /* ERROR invalid map key */ ]M0
|
||||
|
||||
// channels
|
||||
C0 chan C0
|
||||
@ -71,23 +71,23 @@ type (
|
||||
|
||||
func _() {
|
||||
type (
|
||||
t1 /* ERROR "cycle" */ t1
|
||||
t1 /* ERROR cycle */ t1
|
||||
t2 *t2
|
||||
|
||||
t3 t4 /* ERROR "undeclared" */
|
||||
t4 t5 /* ERROR "undeclared" */
|
||||
t3 t4 /* ERROR undeclared */
|
||||
t4 t5 /* ERROR undeclared */
|
||||
t5 t3
|
||||
|
||||
// arrays
|
||||
a0 /* ERROR "cycle" */ [10]a0
|
||||
a0 /* ERROR cycle */ [10]a0
|
||||
a1 [10]*a1
|
||||
|
||||
// slices
|
||||
l0 []l0
|
||||
|
||||
// structs
|
||||
s0 /* ERROR "cycle" */ struct{ _ s0 }
|
||||
s1 /* ERROR "cycle" */ struct{ s1 }
|
||||
s0 /* ERROR cycle */ struct{ _ s0 }
|
||||
s1 /* ERROR cycle */ struct{ s1 }
|
||||
s2 struct{ _ *s2 }
|
||||
s3 struct{ *s3 }
|
||||
|
||||
@ -100,10 +100,10 @@ func _() {
|
||||
f2 func(f2) f2
|
||||
|
||||
// interfaces
|
||||
i0 /* ERROR "cycle" */ interface{ i0 }
|
||||
i0 /* ERROR cycle */ interface{ i0 }
|
||||
|
||||
// maps
|
||||
m0 map[m0 /* ERROR "invalid map key" */ ]m0
|
||||
m0 map[m0 /* ERROR invalid map key */ ]m0
|
||||
|
||||
// channels
|
||||
c0 chan c0
|
||||
@ -117,3 +117,27 @@ type A [10]map[A /* ERROR invalid map key */ ]bool
|
||||
type S struct {
|
||||
m map[S /* ERROR invalid map key */ ]bool
|
||||
}
|
||||
|
||||
// test cases for issue 7236
|
||||
// (cycle detection must not be dependent on starting point of resolution)
|
||||
|
||||
type (
|
||||
P1 *T9
|
||||
T9 /* ERROR cycle */ T9
|
||||
|
||||
T10 /* ERROR cycle */ T10
|
||||
P2 *T10
|
||||
)
|
||||
|
||||
func (T11) m() {}
|
||||
|
||||
type T11 /* ERROR cycle */ struct{ T11 }
|
||||
|
||||
type T12 /* ERROR cycle */ struct{ T12 }
|
||||
|
||||
func (*T12) m() {}
|
||||
|
||||
type (
|
||||
P3 *T13
|
||||
T13 /* ERROR cycle */ T13
|
||||
)
|
56
go/types/testdata/cycles2.src
vendored
56
go/types/testdata/cycles2.src
vendored
@ -4,6 +4,8 @@
|
||||
|
||||
package p
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Test case for issue 5090
|
||||
|
||||
type t interface {
|
||||
@ -61,6 +63,56 @@ var _ = x /* ERROR cannot compare */ == y
|
||||
|
||||
// Test case for issue 6638.
|
||||
|
||||
type T /* ERROR cycle */ interface {
|
||||
m() [T /* ERROR no field or method */ (nil).m()[0]]int
|
||||
type T interface {
|
||||
m() [T /* ERROR no value */ (nil).m()[0]]int
|
||||
}
|
||||
|
||||
// Variations of this test case.
|
||||
|
||||
type T1 interface {
|
||||
m() [x1 /* ERROR no value */ .m()[0]]int
|
||||
}
|
||||
|
||||
var x1 T1
|
||||
|
||||
type T2 interface {
|
||||
m() [len(x2 /* ERROR no value */ .m())]int
|
||||
}
|
||||
|
||||
var x2 T2
|
||||
|
||||
type T3 interface {
|
||||
m() [unsafe.Sizeof(x3.m)]int
|
||||
}
|
||||
|
||||
var x3 T3
|
||||
|
||||
// The test case below should also report an error for
|
||||
// the cast inside the T4 interface (like it does for the
|
||||
// variable initialization). The reason why it does not is
|
||||
// that inside T4, the method x4.m depends on T4 which is not
|
||||
// fully set up yet. The x4.m method happens to have an empty
|
||||
// signature which is why the cast is permitted.
|
||||
// TODO(gri) Consider marking methods as incomplete and provide
|
||||
// a better error message in that case.
|
||||
|
||||
type T4 interface {
|
||||
m() [unsafe.Sizeof(cast4(x4.m))]int
|
||||
}
|
||||
|
||||
var x4 T4
|
||||
var _ = cast4(x4 /* ERROR cannot convert */.m)
|
||||
|
||||
type cast4 func()
|
||||
|
||||
// This test is symmetric to the T4 case: Here the cast is
|
||||
// "correct", but it doesn't work inside the T5 interface.
|
||||
|
||||
type T5 interface {
|
||||
m() [unsafe.Sizeof(cast5(x5 /* ERROR cannot convert */ .m))]int
|
||||
}
|
||||
|
||||
var x5 T5
|
||||
var _ = cast5(x5.m)
|
||||
|
||||
type cast5 func() [0]int
|
||||
|
2
go/types/testdata/cycles3.src
vendored
2
go/types/testdata/cycles3.src
vendored
@ -48,7 +48,7 @@ type (
|
||||
)
|
||||
|
||||
type (
|
||||
U /* ERROR illegal cycle */ interface {
|
||||
U interface {
|
||||
V
|
||||
}
|
||||
|
||||
|
@ -365,8 +365,7 @@ func (c *Chan) Elem() Type { return c.elem }
|
||||
// A Named represents a named type.
|
||||
type Named struct {
|
||||
obj *TypeName // corresponding declared object
|
||||
underlying Type // possibly a *Named if !complete; never a *Named if complete
|
||||
complete bool // if set, the underlying type has been determined
|
||||
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
||||
methods []*Func // methods declared for this type (not the method set of this type)
|
||||
mset, pmset cachedMethodSet // method set for T, *T, lazily initialized
|
||||
}
|
||||
@ -377,7 +376,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||
if _, ok := underlying.(*Named); ok {
|
||||
panic("types.NewNamed: underlying type must not be *Named")
|
||||
}
|
||||
typ := &Named{obj: obj, underlying: underlying, complete: underlying != nil, methods: methods}
|
||||
typ := &Named{obj: obj, underlying: underlying, methods: methods}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
@ -403,7 +402,6 @@ func (t *Named) SetUnderlying(underlying Type) {
|
||||
panic("types.Named.SetUnderlying: underlying type must not be *Named")
|
||||
}
|
||||
t.underlying = underlying
|
||||
t.complete = true
|
||||
}
|
||||
|
||||
// AddMethod adds method m unless it is already in the method list.
|
||||
|
@ -17,9 +17,9 @@ import (
|
||||
|
||||
// ident type-checks identifier e and initializes x with the value or type of e.
|
||||
// If an error occurred, x.mode is set to invalid.
|
||||
// For the meaning of def and cycleOk, see check.typ, below.
|
||||
// For the meaning of def and cycle, see check.typ, below.
|
||||
//
|
||||
func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) {
|
||||
func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycle []*TypeName) {
|
||||
x.mode = invalid
|
||||
x.expr = e
|
||||
|
||||
@ -41,7 +41,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
|
||||
check.dump("%s: %s should have been declared (we are inside a function)", e.Pos(), e)
|
||||
unreachable()
|
||||
}
|
||||
check.objDecl(obj, def, cycleOk)
|
||||
check.objDecl(obj, def, cycle)
|
||||
typ = obj.Type()
|
||||
}
|
||||
assert(typ != nil)
|
||||
@ -76,11 +76,16 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
|
||||
case *TypeName:
|
||||
obj.used = true
|
||||
x.mode = typexpr
|
||||
named, _ := typ.(*Named)
|
||||
if !cycleOk && named != nil && !named.complete {
|
||||
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
|
||||
// maintain x.mode == typexpr despite error
|
||||
typ = Typ[Invalid]
|
||||
// check for cycle
|
||||
// TODO(gri) consider passing []*Named instead of []*TypeName for cycle
|
||||
for _, prev := range cycle {
|
||||
if prev == obj {
|
||||
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
|
||||
// TODO(gri) print the actual cycle
|
||||
// maintain x.mode == typexpr despite error
|
||||
typ = Typ[Invalid]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case *Var:
|
||||
@ -117,10 +122,10 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
|
||||
// If def != nil, e is the type specification for the named type def, declared
|
||||
// in a type declaration, and def.underlying will be set to the type of e before
|
||||
// any components of e are type-checked.
|
||||
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
|
||||
// If cycle is not empty, e (or elements of e) may refer to a named type that is not
|
||||
// yet completely set up.
|
||||
//
|
||||
func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) (T Type) {
|
||||
func (check *checker) typ(e ast.Expr, def *Named, cycle []*TypeName) (T Type) {
|
||||
if trace {
|
||||
check.trace(e.Pos(), "%s", e)
|
||||
check.indent++
|
||||
@ -130,13 +135,16 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) (T Type) {
|
||||
}()
|
||||
}
|
||||
|
||||
T = check.typInternal(e, def, cycleOk)
|
||||
T = check.typInternal(e, def, cycle)
|
||||
assert(isTyped(T))
|
||||
check.recordTypeAndValue(e, T, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(gri) provide a convenience function (say, check.typExpr) that works
|
||||
// as check.typ but only takes an ast.Expr and assumes nil for def and cycle.
|
||||
|
||||
// funcType type-checks a function or method type and returns its signature.
|
||||
func (check *checker) funcType(sig *Signature, recv *ast.FieldList, ftyp *ast.FuncType) *Signature {
|
||||
scope := NewScope(check.topScope)
|
||||
@ -197,14 +205,14 @@ func (check *checker) funcType(sig *Signature, recv *ast.FieldList, ftyp *ast.Fu
|
||||
// typInternal drives type checking of types.
|
||||
// Must only be called by typ.
|
||||
//
|
||||
func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||
func (check *checker) typInternal(e ast.Expr, def *Named, cycle []*TypeName) Type {
|
||||
switch e := e.(type) {
|
||||
case *ast.BadExpr:
|
||||
// ignore - error reported before
|
||||
|
||||
case *ast.Ident:
|
||||
var x operand
|
||||
check.ident(&x, e, def, cycleOk)
|
||||
check.ident(&x, e, def, cycle)
|
||||
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
@ -237,33 +245,33 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||
}
|
||||
|
||||
case *ast.ParenExpr:
|
||||
return check.typ(e.X, def, cycleOk)
|
||||
return check.typ(e.X, def, cycle)
|
||||
|
||||
case *ast.ArrayType:
|
||||
if e.Len != nil {
|
||||
typ := new(Array)
|
||||
def.setUnderlying(typ)
|
||||
typ.len = check.arrayLength(e.Len)
|
||||
typ.elem = check.typ(e.Elt, nil, cycleOk)
|
||||
typ.elem = check.typ(e.Elt, nil, cycle)
|
||||
return typ
|
||||
|
||||
} else {
|
||||
typ := new(Slice)
|
||||
def.setUnderlying(typ)
|
||||
typ.elem = check.typ(e.Elt, nil, true)
|
||||
typ.elem = check.typ(e.Elt, nil, nil)
|
||||
return typ
|
||||
}
|
||||
|
||||
case *ast.StructType:
|
||||
typ := new(Struct)
|
||||
def.setUnderlying(typ)
|
||||
check.structType(typ, e, cycleOk)
|
||||
check.structType(typ, e, cycle)
|
||||
return typ
|
||||
|
||||
case *ast.StarExpr:
|
||||
typ := new(Pointer)
|
||||
def.setUnderlying(typ)
|
||||
typ.base = check.typ(e.X, nil, true)
|
||||
typ.base = check.typ(e.X, nil, nil)
|
||||
return typ
|
||||
|
||||
case *ast.FuncType:
|
||||
@ -275,15 +283,15 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||
case *ast.InterfaceType:
|
||||
typ := new(Interface)
|
||||
def.setUnderlying(typ)
|
||||
check.interfaceType(typ, e, def, cycleOk)
|
||||
check.interfaceType(typ, e, def, cycle)
|
||||
return typ
|
||||
|
||||
case *ast.MapType:
|
||||
typ := new(Map)
|
||||
def.setUnderlying(typ)
|
||||
|
||||
typ.key = check.typ(e.Key, nil, true)
|
||||
typ.elem = check.typ(e.Value, nil, true)
|
||||
typ.key = check.typ(e.Key, nil, nil)
|
||||
typ.elem = check.typ(e.Value, nil, nil)
|
||||
|
||||
// spec: "The comparison operators == and != must be fully defined
|
||||
// for operands of the key type; thus the key type must not be a
|
||||
@ -317,7 +325,7 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
|
||||
}
|
||||
|
||||
typ.dir = dir
|
||||
typ.elem = check.typ(e.Value, nil, true)
|
||||
typ.elem = check.typ(e.Value, nil, nil)
|
||||
return typ
|
||||
|
||||
default:
|
||||
@ -391,7 +399,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
||||
// ignore ... and continue
|
||||
}
|
||||
}
|
||||
typ := check.typ(ftype, nil, true)
|
||||
typ := check.typ(ftype, nil, nil)
|
||||
// The parser ensures that f.Tag is nil and we don't
|
||||
// care if a constructed AST contains a non-nil tag.
|
||||
if len(field.Names) > 0 {
|
||||
@ -427,7 +435,7 @@ func (check *checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool
|
||||
return true
|
||||
}
|
||||
|
||||
func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, cycleOk bool) {
|
||||
func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, cycle []*TypeName) {
|
||||
// empty interface: common case
|
||||
if ityp.Methods == nil {
|
||||
return
|
||||
@ -436,6 +444,12 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
|
||||
// The parser ensures that field tags are nil and we don't
|
||||
// care if a constructed AST contains non-nil tags.
|
||||
|
||||
// use named receiver type if available (for better error messages)
|
||||
var recvTyp Type = iface
|
||||
if def != nil {
|
||||
recvTyp = def
|
||||
}
|
||||
|
||||
// Phase 1: Collect explicitly declared methods, the corresponding
|
||||
// signature (AST) expressions, and the list of embedded
|
||||
// type (AST) expressions. Do not resolve signatures or
|
||||
@ -455,7 +469,15 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
|
||||
pos := name.Pos()
|
||||
// Don't type-check signature yet - use an
|
||||
// empty signature now and update it later.
|
||||
m := NewFunc(pos, check.pkg, name.Name, new(Signature))
|
||||
// Since we know the receiver, set it up now
|
||||
// (required to avoid crash in ptrRecv; see
|
||||
// e.g. test case for issue 6638).
|
||||
// TODO(gri) Consider marking methods signatures
|
||||
// as incomplete, for better error messages. See
|
||||
// also the T4 and T5 tests in testdata/cycles2.src.
|
||||
sig := new(Signature)
|
||||
sig.recv = NewVar(pos, check.pkg, "", recvTyp)
|
||||
m := NewFunc(pos, check.pkg, name.Name, sig)
|
||||
// spec: "As with all method sets, in an interface type,
|
||||
// each method must have a unique name."
|
||||
// (The spec does not exclude blank _ identifiers for
|
||||
@ -484,7 +506,7 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
|
||||
|
||||
for _, e := range embedded {
|
||||
pos := e.Pos()
|
||||
typ := check.typ(e, nil, cycleOk)
|
||||
typ := check.typ(e, nil, cycle)
|
||||
named, _ := typ.(*Named)
|
||||
if named == nil {
|
||||
if typ != Typ[Invalid] {
|
||||
@ -517,16 +539,9 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
|
||||
// and embed the methods of this interface in a parameter of interface
|
||||
// type.
|
||||
|
||||
// determine receiver type
|
||||
var recv Type = iface
|
||||
if def != nil {
|
||||
def.underlying = iface
|
||||
recv = def // use named receiver type if available
|
||||
}
|
||||
|
||||
for i, m := range iface.methods {
|
||||
expr := signatures[i]
|
||||
typ := check.typ(expr, nil, true)
|
||||
typ := check.typ(expr, nil, nil)
|
||||
sig, _ := typ.(*Signature)
|
||||
if sig == nil {
|
||||
if typ != Typ[Invalid] {
|
||||
@ -534,8 +549,10 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
|
||||
}
|
||||
continue // keep method with empty method signature
|
||||
}
|
||||
sig.recv = NewVar(m.pos, check.pkg, "", recv)
|
||||
*m.typ.(*Signature) = *sig // update signature (don't replace it!)
|
||||
// update signature, but keep recv that was set up before
|
||||
old := m.typ.(*Signature)
|
||||
sig.recv = old.recv
|
||||
*old = *sig // update signature (don't replace it!)
|
||||
}
|
||||
|
||||
// TODO(gri) The list of explicit methods is only sorted for now to
|
||||
@ -577,7 +594,7 @@ func (check *checker) tag(t *ast.BasicLit) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *checker) structType(styp *Struct, e *ast.StructType, cycleOk bool) {
|
||||
func (check *checker) structType(styp *Struct, e *ast.StructType, cycle []*TypeName) {
|
||||
list := e.Fields
|
||||
if list == nil {
|
||||
return
|
||||
@ -612,7 +629,7 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, cycleOk bool)
|
||||
}
|
||||
|
||||
for _, f := range list.List {
|
||||
typ = check.typ(f.Type, nil, cycleOk)
|
||||
typ = check.typ(f.Type, nil, cycle)
|
||||
tag = check.tag(f.Tag)
|
||||
if len(f.Names) > 0 {
|
||||
// named fields
|
||||
|
@ -69,7 +69,7 @@ func defPredeclaredTypes() {
|
||||
res := NewVar(token.NoPos, nil, "", Typ[String])
|
||||
sig := &Signature{results: NewTuple(res)}
|
||||
err := NewFunc(token.NoPos, nil, "Error", sig)
|
||||
typ := &Named{underlying: NewInterface([]*Func{err}, nil), complete: true}
|
||||
typ := &Named{underlying: NewInterface([]*Func{err}, nil)}
|
||||
sig.recv = NewVar(token.NoPos, nil, "", typ)
|
||||
def(NewTypeName(token.NoPos, nil, "error", typ))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user