With this change validType doesn't modify global state anymore.
It also eliminates the need for an extra field in each object.
Preparation for fixing issue #48962.
Change-Id: If241ec77ff48911d5b43d89adabfb8ef54452c6b
Reviewed-on: https://go-review.googlesource.com/c/go/+/378176
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>
nextID uint64 // unique Id for type parameters (first valid Id is 1)
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection)
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
+ infoMap: make(map[*Named]typeInfo),
}
}
var rhs Type
check.later(func() {
- check.validType(obj.typ, nil)
+ check.validType(obj.typ)
// If typ is local, an error was already reported where typ is specified/defined.
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs)
// A Named represents a named (defined) type.
type Named struct {
check *Checker
- info typeInfo // for cycle detection
obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types
orig *Named // original, uninstantiated type
fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
{Interface{}, 44, 88},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 68, 128},
+ {Named{}, 64, 120},
{TypeParam{}, 28, 48},
{term{}, 12, 24},
}
}
- check.validType(inst, nil)
+ check.validType(inst)
})
return inst
package types2
-type typeInfo uint
-
-// validType verifies that the given type does not "expand" infinitely
+// validType verifies that the given type does not "expand" indefinitely
// 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 {
+func (check *Checker) validType(typ Type) {
+ check.validType0(typ, nil)
+}
+
+type typeInfo uint
+
+func (check *Checker) validType0(typ Type, path []Object) typeInfo {
const (
unknown typeInfo = iota
marked
switch t := typ.(type) {
case *Array:
- return check.validType(t.elem, path)
+ return check.validType0(t.elem, path)
case *Struct:
for _, f := range t.fields {
- if check.validType(f.typ, path) == invalid {
+ if check.validType0(f.typ, path) == invalid {
return invalid
}
}
case *Union:
for _, t := range t.terms {
- if check.validType(t.typ, path) == invalid {
+ if check.validType0(t.typ, path) == invalid {
return invalid
}
}
case *Interface:
for _, etyp := range t.embeddeds {
- if check.validType(etyp, path) == invalid {
+ if check.validType0(etyp, path) == invalid {
return invalid
}
}
// 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
+ check.infoMap[t] = invalid
return invalid
}
- switch t.info {
+ switch check.infoMap[t] {
case unknown:
- t.info = marked
- t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
+ check.infoMap[t] = marked
+ check.infoMap[t] = check.validType0(t.fromRHS, append(path, t.obj)) // only types of current package added to path
case marked:
// cycle detected
for i, tn := range path {
}
if tn == t.obj {
check.cycleError(path[i:])
- t.info = invalid
+ check.infoMap[t] = invalid
t.underlying = Typ[Invalid]
return invalid
}
}
panic("cycle start not found")
}
- return t.info
+ return check.infoMap[t]
}
return valid
nextID uint64 // unique Id for type parameters (first valid Id is 1)
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ infoMap map[*Named]typeInfo // maps named types to their associated type info (for cycle detection)
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
+ infoMap: make(map[*Named]typeInfo),
}
}
var rhs Type
check.later(func() {
- check.validType(obj.typ, nil)
+ check.validType(obj.typ)
// If typ is local, an error was already reported where typ is specified/defined.
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(tdecl.Type, _UnsupportedFeature, "using type constraint %s requires go1.18 or later", rhs)
// A Named represents a named (defined) type.
type Named struct {
check *Checker
- info typeInfo // for cycle detection
obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types
orig *Named // original, uninstantiated type
fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
{Interface{}, 44, 88},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 68, 128},
+ {Named{}, 64, 120},
{TypeParam{}, 28, 48},
{term{}, 12, 24},
}
}
- check.validType(inst, nil)
+ check.validType(inst)
})
return inst
package types
-type typeInfo uint
-
-// validType verifies that the given type does not "expand" infinitely
+// validType verifies that the given type does not "expand" indefinitely
// 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 {
+func (check *Checker) validType(typ Type) {
+ check.validType0(typ, nil)
+}
+
+type typeInfo uint
+
+func (check *Checker) validType0(typ Type, path []Object) typeInfo {
const (
unknown typeInfo = iota
marked
switch t := typ.(type) {
case *Array:
- return check.validType(t.elem, path)
+ return check.validType0(t.elem, path)
case *Struct:
for _, f := range t.fields {
- if check.validType(f.typ, path) == invalid {
+ if check.validType0(f.typ, path) == invalid {
return invalid
}
}
case *Union:
for _, t := range t.terms {
- if check.validType(t.typ, path) == invalid {
+ if check.validType0(t.typ, path) == invalid {
return invalid
}
}
case *Interface:
for _, etyp := range t.embeddeds {
- if check.validType(etyp, path) == invalid {
+ if check.validType0(etyp, path) == invalid {
return invalid
}
}
// 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
+ check.infoMap[t] = invalid
return invalid
}
- switch t.info {
+ switch check.infoMap[t] {
case unknown:
- t.info = marked
- t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path
+ check.infoMap[t] = marked
+ check.infoMap[t] = check.validType0(t.fromRHS, append(path, t.obj)) // only types of current package added to path
case marked:
// cycle detected
for i, tn := range path {
}
if tn == t.obj {
check.cycleError(path[i:])
- t.info = invalid
+ check.infoMap[t] = invalid
t.underlying = Typ[Invalid]
return invalid
}
}
panic("cycle start not found")
}
- return t.info
+ return check.infoMap[t]
}
return valid