Debugging support.
For #65711.
Change-Id: I2b8b03d2c6e02d32a4f9272313e852f17da35b3e
Reviewed-on: https://go-review.googlesource.com/c/go/+/567975
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
package types2
+import "cmd/compile/internal/syntax"
+
// validType verifies that the given type does not "expand" indefinitely
// producing a cycle in the type graph.
// (Cycles involving alias types, as in "type A = [10]A" are detected
// earlier, via the objDecl cycle detection mechanism.)
func (check *Checker) validType(typ *Named) {
- check.validType0(typ, nil, nil)
+ check.validType0(nopos, typ, nil, nil)
}
// validType0 checks if the given type is valid. If typ is a type parameter
// of) F in S, leading to the nest S->F. If a type appears in its own nest
// (say S->F->S) we have an invalid recursive type. The path list is the full
// path of named types in a cycle, it is only needed for error reporting.
-func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
- switch t := Unalias(typ).(type) {
+func (check *Checker) validType0(pos syntax.Pos, typ Type, nest, path []*Named) bool {
+ typ = Unalias(typ)
+
+ if check.conf.Trace {
+ if t, _ := typ.(*Named); t != nil && t.obj != nil /* obj should always exist but be conservative */ {
+ pos = t.obj.pos
+ }
+ check.indent++
+ check.trace(pos, "validType(%s) nest %v, path %v", typ, pathString(makeObjList(nest)), pathString(makeObjList(path)))
+ defer func() {
+ check.indent--
+ }()
+ }
+
+ switch t := typ.(type) {
case nil:
// We should never see a nil type but be conservative and panic
// only in debug mode.
}
case *Array:
- return check.validType0(t.elem, nest, path)
+ return check.validType0(pos, t.elem, nest, path)
case *Struct:
for _, f := range t.fields {
- if !check.validType0(f.typ, nest, path) {
+ if !check.validType0(pos, f.typ, nest, path) {
return false
}
}
case *Union:
for _, t := range t.terms {
- if !check.validType0(t.typ, nest, path) {
+ if !check.validType0(pos, t.typ, nest, path) {
return false
}
}
case *Interface:
for _, etyp := range t.embeddeds {
- if !check.validType0(etyp, nest, path) {
+ if !check.validType0(pos, etyp, nest, path) {
return false
}
}
// Every type added to nest is also added to path; thus every type that is in nest
// must also be in path (invariant). But not every type in path is in nest, since
// nest may be pruned (see below, *TypeParam case).
- if !check.validType0(t.Origin().fromRHS, append(nest, t), append(path, t)) {
+ if !check.validType0(pos, t.Origin().fromRHS, append(nest, t), append(path, t)) {
return false
}
// the current (instantiated) type (see the example
// at the end of this file).
// For error reporting we keep the full path.
- return check.validType0(targ, nest[:len(nest)-1], path)
+ return check.validType0(pos, targ, nest[:len(nest)-1], path)
}
}
}
"unify.go": fixSprintf,
"universe.go": fixGlobalTypVarDecl,
"util_test.go": fixTokenPos,
- "validtype.go": nil,
+ "validtype.go": func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
}
// TODO(gri) We should be able to make these rewriters more configurable/composable.
package types
+import "go/token"
+
// validType verifies that the given type does not "expand" indefinitely
// producing a cycle in the type graph.
// (Cycles involving alias types, as in "type A = [10]A" are detected
// earlier, via the objDecl cycle detection mechanism.)
func (check *Checker) validType(typ *Named) {
- check.validType0(typ, nil, nil)
+ check.validType0(nopos, typ, nil, nil)
}
// validType0 checks if the given type is valid. If typ is a type parameter
// of) F in S, leading to the nest S->F. If a type appears in its own nest
// (say S->F->S) we have an invalid recursive type. The path list is the full
// path of named types in a cycle, it is only needed for error reporting.
-func (check *Checker) validType0(typ Type, nest, path []*Named) bool {
- switch t := Unalias(typ).(type) {
+func (check *Checker) validType0(pos token.Pos, typ Type, nest, path []*Named) bool {
+ typ = Unalias(typ)
+
+ if check.conf._Trace {
+ if t, _ := typ.(*Named); t != nil && t.obj != nil /* obj should always exist but be conservative */ {
+ pos = t.obj.pos
+ }
+ check.indent++
+ check.trace(pos, "validType(%s) nest %v, path %v", typ, pathString(makeObjList(nest)), pathString(makeObjList(path)))
+ defer func() {
+ check.indent--
+ }()
+ }
+
+ switch t := typ.(type) {
case nil:
// We should never see a nil type but be conservative and panic
// only in debug mode.
}
case *Array:
- return check.validType0(t.elem, nest, path)
+ return check.validType0(pos, t.elem, nest, path)
case *Struct:
for _, f := range t.fields {
- if !check.validType0(f.typ, nest, path) {
+ if !check.validType0(pos, f.typ, nest, path) {
return false
}
}
case *Union:
for _, t := range t.terms {
- if !check.validType0(t.typ, nest, path) {
+ if !check.validType0(pos, t.typ, nest, path) {
return false
}
}
case *Interface:
for _, etyp := range t.embeddeds {
- if !check.validType0(etyp, nest, path) {
+ if !check.validType0(pos, etyp, nest, path) {
return false
}
}
// Every type added to nest is also added to path; thus every type that is in nest
// must also be in path (invariant). But not every type in path is in nest, since
// nest may be pruned (see below, *TypeParam case).
- if !check.validType0(t.Origin().fromRHS, append(nest, t), append(path, t)) {
+ if !check.validType0(pos, t.Origin().fromRHS, append(nest, t), append(path, t)) {
return false
}
// the current (instantiated) type (see the example
// at the end of this file).
// For error reporting we keep the full path.
- return check.validType0(targ, nest[:len(nest)-1], path)
+ return check.validType0(pos, targ, nest[:len(nest)-1], path)
}
}
}