mirror of https://github.com/golang/go.git
go/types, types2: move validType code into its own file
The validType check is independent of the work of declaring objects. Move it into a separate file for better separation of concerns and code organization. No other changes - this is purely a code move. Preparation for fixing issue #48962. Change-Id: Ib08db2d009c4890882d0978b278e965ca3078851 Reviewed-on: https://go-review.googlesource.com/c/go/+/378674 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
b850f3629f
commit
0328b4f4ca
|
|
@ -304,96 +304,6 @@ loop:
|
|||
return false
|
||||
}
|
||||
|
||||
type typeInfo uint
|
||||
|
||||
// validType verifies that the given type does not "expand" infinitely
|
||||
// producing a cycle in the type graph. Cycles are detected by marking
|
||||
// defined types.
|
||||
// (Cycles involving alias types, as in "type A = [10]A" are detected
|
||||
// earlier, via the objDecl cycle detection mechanism.)
|
||||
func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||
const (
|
||||
unknown typeInfo = iota
|
||||
marked
|
||||
valid
|
||||
invalid
|
||||
)
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *Array:
|
||||
return check.validType(t.elem, path)
|
||||
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if check.validType(f.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Union:
|
||||
for _, t := range t.terms {
|
||||
if check.validType(t.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
for _, etyp := range t.embeddeds {
|
||||
if check.validType(etyp, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Named:
|
||||
// If t is parameterized, we should be considering the instantiated (expanded)
|
||||
// form of t, but in general we can't with this algorithm: if t is an invalid
|
||||
// type it may be so because it infinitely expands through a type parameter.
|
||||
// Instantiating such a type would lead to an infinite sequence of instantiations.
|
||||
// In general, we need "type flow analysis" to recognize those cases.
|
||||
// Example: type A[T any] struct{ x A[*T] } (issue #48951)
|
||||
// In this algorithm we always only consider the original, uninstantiated type.
|
||||
// This won't recognize some invalid cases with parameterized types, but it
|
||||
// will terminate.
|
||||
t = t.orig
|
||||
|
||||
// don't touch the type if it is from a different package or the Universe scope
|
||||
// (doing so would lead to a race condition - was issue #35049)
|
||||
if t.obj.pkg != check.pkg {
|
||||
return valid
|
||||
}
|
||||
|
||||
// don't report a 2nd error if we already know the type is invalid
|
||||
// (e.g., if a cycle was detected earlier, via under).
|
||||
if t.underlying == Typ[Invalid] {
|
||||
t.info = invalid
|
||||
return invalid
|
||||
}
|
||||
|
||||
switch t.info {
|
||||
case unknown:
|
||||
t.info = marked
|
||||
t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
|
||||
case marked:
|
||||
// cycle detected
|
||||
for i, tn := range path {
|
||||
if t.obj.pkg != check.pkg {
|
||||
panic("type cycle via package-external type")
|
||||
}
|
||||
if tn == t.obj {
|
||||
check.cycleError(path[i:])
|
||||
t.info = invalid
|
||||
t.underlying = Typ[Invalid]
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
panic("cycle start not found")
|
||||
}
|
||||
return t.info
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
// cycleError reports a declaration cycle starting with
|
||||
// the object in cycle that is "first" in the source.
|
||||
func (check *Checker) cycleError(cycle []Object) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types2
|
||||
|
||||
type typeInfo uint
|
||||
|
||||
// validType verifies that the given type does not "expand" infinitely
|
||||
// producing a cycle in the type graph. Cycles are detected by marking
|
||||
// defined types.
|
||||
// (Cycles involving alias types, as in "type A = [10]A" are detected
|
||||
// earlier, via the objDecl cycle detection mechanism.)
|
||||
func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||
const (
|
||||
unknown typeInfo = iota
|
||||
marked
|
||||
valid
|
||||
invalid
|
||||
)
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *Array:
|
||||
return check.validType(t.elem, path)
|
||||
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if check.validType(f.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Union:
|
||||
for _, t := range t.terms {
|
||||
if check.validType(t.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
for _, etyp := range t.embeddeds {
|
||||
if check.validType(etyp, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Named:
|
||||
// If t is parameterized, we should be considering the instantiated (expanded)
|
||||
// form of t, but in general we can't with this algorithm: if t is an invalid
|
||||
// type it may be so because it infinitely expands through a type parameter.
|
||||
// Instantiating such a type would lead to an infinite sequence of instantiations.
|
||||
// In general, we need "type flow analysis" to recognize those cases.
|
||||
// Example: type A[T any] struct{ x A[*T] } (issue #48951)
|
||||
// In this algorithm we always only consider the original, uninstantiated type.
|
||||
// This won't recognize some invalid cases with parameterized types, but it
|
||||
// will terminate.
|
||||
t = t.orig
|
||||
|
||||
// don't touch the type if it is from a different package or the Universe scope
|
||||
// (doing so would lead to a race condition - was issue #35049)
|
||||
if t.obj.pkg != check.pkg {
|
||||
return valid
|
||||
}
|
||||
|
||||
// don't report a 2nd error if we already know the type is invalid
|
||||
// (e.g., if a cycle was detected earlier, via under).
|
||||
if t.underlying == Typ[Invalid] {
|
||||
t.info = invalid
|
||||
return invalid
|
||||
}
|
||||
|
||||
switch t.info {
|
||||
case unknown:
|
||||
t.info = marked
|
||||
t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
|
||||
case marked:
|
||||
// cycle detected
|
||||
for i, tn := range path {
|
||||
if t.obj.pkg != check.pkg {
|
||||
panic("type cycle via package-external type")
|
||||
}
|
||||
if tn == t.obj {
|
||||
check.cycleError(path[i:])
|
||||
t.info = invalid
|
||||
t.underlying = Typ[Invalid]
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
panic("cycle start not found")
|
||||
}
|
||||
return t.info
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
|
@ -303,96 +303,6 @@ loop:
|
|||
return false
|
||||
}
|
||||
|
||||
type typeInfo uint
|
||||
|
||||
// validType verifies that the given type does not "expand" infinitely
|
||||
// producing a cycle in the type graph. Cycles are detected by marking
|
||||
// defined types.
|
||||
// (Cycles involving alias types, as in "type A = [10]A" are detected
|
||||
// earlier, via the objDecl cycle detection mechanism.)
|
||||
func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||
const (
|
||||
unknown typeInfo = iota
|
||||
marked
|
||||
valid
|
||||
invalid
|
||||
)
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *Array:
|
||||
return check.validType(t.elem, path)
|
||||
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if check.validType(f.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Union:
|
||||
for _, t := range t.terms {
|
||||
if check.validType(t.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
for _, etyp := range t.embeddeds {
|
||||
if check.validType(etyp, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Named:
|
||||
// If t is parameterized, we should be considering the instantiated (expanded)
|
||||
// form of t, but in general we can't with this algorithm: if t is an invalid
|
||||
// type it may be so because it infinitely expands through a type parameter.
|
||||
// Instantiating such a type would lead to an infinite sequence of instantiations.
|
||||
// In general, we need "type flow analysis" to recognize those cases.
|
||||
// Example: type A[T any] struct{ x A[*T] } (issue #48951)
|
||||
// In this algorithm we always only consider the original, uninstantiated type.
|
||||
// This won't recognize some invalid cases with parameterized types, but it
|
||||
// will terminate.
|
||||
t = t.orig
|
||||
|
||||
// don't touch the type if it is from a different package or the Universe scope
|
||||
// (doing so would lead to a race condition - was issue #35049)
|
||||
if t.obj.pkg != check.pkg {
|
||||
return valid
|
||||
}
|
||||
|
||||
// don't report a 2nd error if we already know the type is invalid
|
||||
// (e.g., if a cycle was detected earlier, via under).
|
||||
if t.underlying == Typ[Invalid] {
|
||||
t.info = invalid
|
||||
return invalid
|
||||
}
|
||||
|
||||
switch t.info {
|
||||
case unknown:
|
||||
t.info = marked
|
||||
t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
|
||||
case marked:
|
||||
// cycle detected
|
||||
for i, tn := range path {
|
||||
if t.obj.pkg != check.pkg {
|
||||
panic("type cycle via package-external type")
|
||||
}
|
||||
if tn == t.obj {
|
||||
check.cycleError(path[i:])
|
||||
t.info = invalid
|
||||
t.underlying = Typ[Invalid]
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
panic("cycle start not found")
|
||||
}
|
||||
return t.info
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
// cycleError reports a declaration cycle starting with
|
||||
// the object in cycle that is "first" in the source.
|
||||
func (check *Checker) cycleError(cycle []Object) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
type typeInfo uint
|
||||
|
||||
// validType verifies that the given type does not "expand" infinitely
|
||||
// producing a cycle in the type graph. Cycles are detected by marking
|
||||
// defined types.
|
||||
// (Cycles involving alias types, as in "type A = [10]A" are detected
|
||||
// earlier, via the objDecl cycle detection mechanism.)
|
||||
func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||
const (
|
||||
unknown typeInfo = iota
|
||||
marked
|
||||
valid
|
||||
invalid
|
||||
)
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *Array:
|
||||
return check.validType(t.elem, path)
|
||||
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if check.validType(f.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Union:
|
||||
for _, t := range t.terms {
|
||||
if check.validType(t.typ, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
for _, etyp := range t.embeddeds {
|
||||
if check.validType(etyp, path) == invalid {
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
|
||||
case *Named:
|
||||
// If t is parameterized, we should be considering the instantiated (expanded)
|
||||
// form of t, but in general we can't with this algorithm: if t is an invalid
|
||||
// type it may be so because it infinitely expands through a type parameter.
|
||||
// Instantiating such a type would lead to an infinite sequence of instantiations.
|
||||
// In general, we need "type flow analysis" to recognize those cases.
|
||||
// Example: type A[T any] struct{ x A[*T] } (issue #48951)
|
||||
// In this algorithm we always only consider the original, uninstantiated type.
|
||||
// This won't recognize some invalid cases with parameterized types, but it
|
||||
// will terminate.
|
||||
t = t.orig
|
||||
|
||||
// don't touch the type if it is from a different package or the Universe scope
|
||||
// (doing so would lead to a race condition - was issue #35049)
|
||||
if t.obj.pkg != check.pkg {
|
||||
return valid
|
||||
}
|
||||
|
||||
// don't report a 2nd error if we already know the type is invalid
|
||||
// (e.g., if a cycle was detected earlier, via under).
|
||||
if t.underlying == Typ[Invalid] {
|
||||
t.info = invalid
|
||||
return invalid
|
||||
}
|
||||
|
||||
switch t.info {
|
||||
case unknown:
|
||||
t.info = marked
|
||||
t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
|
||||
case marked:
|
||||
// cycle detected
|
||||
for i, tn := range path {
|
||||
if t.obj.pkg != check.pkg {
|
||||
panic("type cycle via package-external type")
|
||||
}
|
||||
if tn == t.obj {
|
||||
check.cycleError(path[i:])
|
||||
t.info = invalid
|
||||
t.underlying = Typ[Invalid]
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
panic("cycle start not found")
|
||||
}
|
||||
return t.info
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
Loading…
Reference in New Issue