1
0
mirror of https://github.com/golang/go synced 2024-09-29 22:24:33 -06:00

cmd/compile/internal/types2: add debugging support for delayed actions

Add a simple mechanism to provide formatted descriptions for
delayed actions. The comment strings are printed when tracing
is enabled and the delayed action is executed. This results
in more easily decipherable tracing output.

Requires debug mode in order to minimize the overhead during normal
execution. Use the mechanism in a few places to show typical use.

Also cleaned up a few unrelated comments.

Change-Id: Ic273c380c3963341500396ec62b694d143c25de2
Reviewed-on: https://go-review.googlesource.com/c/go/+/355871
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-10-13 18:50:06 -07:00
parent a80e53ec43
commit 8c99421f01
6 changed files with 40 additions and 11 deletions

View File

@ -74,6 +74,28 @@ type dotImportKey struct {
name string
}
// An action describes a (delayed) action.
type action struct {
f func() // action to be executed
desc *actionDesc // action description; may be nil, requires debug to be set
}
// If debug is set, describef sets a printf-formatted description for action a.
// Otherwise, it is a no-op.
func (a *action) describef(pos poser, format string, args ...interface{}) {
if debug {
a.desc = &actionDesc{pos, format, args}
}
}
// An actionDesc provides information on an action.
// For debugging only.
type actionDesc struct {
pos poser
format string
args []interface{}
}
// A Checker maintains the state of the type checker.
// It must be created with NewChecker.
type Checker struct {
@ -108,7 +130,7 @@ type Checker struct {
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
untyped map[syntax.Expr]exprInfo // map of expressions without final type
delayed []func() // stack of delayed action segments; segments are processed in FIFO order
delayed []action // stack of delayed action segments; segments are processed in FIFO order
objPath []Object // path of object dependencies during type inference (for cycle reporting)
// context within which the current object is type-checked
@ -144,8 +166,12 @@ func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode,
// either at the end of the current statement, or in case of a local constant
// or variable declaration, before the constant or variable is in scope
// (so that f still sees the scope before any new declarations).
func (check *Checker) later(f func()) {
check.delayed = append(check.delayed, f)
// later returns the pushed action so one can provide a description
// via action.describef for debugging, if desired.
func (check *Checker) later(f func()) *action {
i := len(check.delayed)
check.delayed = append(check.delayed, action{f: f})
return &check.delayed[i]
}
// push pushes obj onto the object path and returns its index in the path.
@ -259,6 +285,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print := func(msg string) {
if check.conf.Trace {
fmt.Println()
fmt.Println(msg)
}
}
@ -309,7 +336,12 @@ func (check *Checker) processDelayed(top int) {
// add more actions (such as nested functions), so
// this is a sufficiently bounded process.
for i := top; i < len(check.delayed); i++ {
check.delayed[i]() // may append to check.delayed
a := &check.delayed[i]
if check.conf.Trace && a.desc != nil {
fmt.Println()
check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...)
}
a.f() // may append to check.delayed
}
assert(top <= len(check.delayed)) // stack must not have shrunk
check.delayed = check.delayed[:top]

View File

@ -557,7 +557,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(tdecl.Type.Pos(), "using type constraint %s requires go1.18 or later", rhs)
}
})
}).describef(obj, "validType(%s)", obj.Name())
alias := tdecl.Alias
if alias && tdecl.TParamList != nil {

View File

@ -63,7 +63,6 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
if res != nil {
// Calling under() here may lead to endless instantiations.
// Test case: type T[P any] T[P]
// TODO(gri) investigate if that's a bug or to be expected.
under = safeUnderlying(res)
}
check.trace(pos, "=> %s (under = %s)", res, under)
@ -115,7 +114,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Con
}
}
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
named := check.newNamed(tname, t, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
named.targs = NewTypeList(targs)
named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
return expandNamed(ctxt, n, pos)

View File

@ -172,7 +172,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
check.later(func() {
computeInterfaceTypeSet(check, iface.Pos(), ityp)
ityp.check = nil
})
}).describef(iface, "compute type set for %s", ityp)
}
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {

View File

@ -154,7 +154,7 @@ func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
}
}
})
}).describef(embeddedPos, "check embedded type %s", embeddedTyp)
}
}

View File

@ -214,8 +214,6 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
if T != nil {
// Calling under() here may lead to endless instantiations.
// Test case: type T[P any] *T[P]
// TODO(gri) investigate if that's a bug or to be expected
// (see also analogous comment in Checker.instantiate).
under = safeUnderlying(T)
}
if T == under {