From: Robert Griesemer Date: Wed, 28 Feb 2024 23:01:25 +0000 (-0800) Subject: go/types, types2: add tracing to Checker.validType X-Git-Tag: go1.23rc1~1042 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a66a3bf494f652bc4fb209d861cbdba1dea71303;p=gostls13.git go/types, types2: add tracing to Checker.validType Debugging support. For #65711. Change-Id: I2b8b03d2c6e02d32a4f9272313e852f17da35b3e Reviewed-on: https://go-review.googlesource.com/c/go/+/567975 Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer Auto-Submit: Robert Griesemer LUCI-TryBot-Result: Go LUCI --- diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index a880a3d933..c5668096a5 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -4,12 +4,14 @@ 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 @@ -22,8 +24,21 @@ func (check *Checker) validType(typ *Named) { // 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. @@ -32,25 +47,25 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { } 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 } } @@ -121,7 +136,7 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { // 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 } @@ -146,7 +161,7 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { // 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) } } } diff --git a/src/go/types/generate_test.go b/src/go/types/generate_test.go index 893280f39e..a85dfc9b42 100644 --- a/src/go/types/generate_test.go +++ b/src/go/types/generate_test.go @@ -173,7 +173,7 @@ var filemap = map[string]action{ "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. diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index 0638714857..66dba2ea4c 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -6,12 +6,14 @@ 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 @@ -24,8 +26,21 @@ func (check *Checker) validType(typ *Named) { // 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. @@ -34,25 +49,25 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { } 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 } } @@ -123,7 +138,7 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { // 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 } @@ -148,7 +163,7 @@ func (check *Checker) validType0(typ Type, nest, path []*Named) bool { // 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) } } }