From e25823edcea364df70b8db7462f53e7dac2b8fca Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 1 Nov 2018 13:46:50 -0700 Subject: [PATCH] cmd/compile/internal/gc: add tracing support to debug type checking The compiler must first be built with the constant enableTrace set to true (typecheck.go). After that, the -t flag becomes available which enables tracing output of type-checking functions. With enableTrace == false, the tracing code becomes dead code and won't affect the compiler. Typical output might look like this: path/y.go:4:6: typecheck 0xc00033e180 DCLTYPE tc=0 path/y.go:4:6: . typecheck1 0xc00033e180 DCLTYPE tc=2 path/y.go:4:6: . . typecheck 0xc000331a40 TYPE T tc=1 path/y.go:4:6: . . . typecheck1 0xc000331a40 TYPE T tc=2 path/y.go:4:6: . . . . typecheckdef 0xc000331a40 TYPE T tc=2 path/y.go:4:6: . . . . => 0xc000331a40 TYPE T tc=2 type=*T path/y.go:4:6: . . . => 0xc000331a40 TYPE T tc=2 type=*T path/y.go:4:6: . . => 0xc000331a40 TYPE T tc=1 type=*T path/y.go:4:6: . => 0xc00033e180 DCLTYPE tc=2 type= path/y.go:4:6: => 0xc00033e180 DCLTYPE tc=1 type= Disabled by default. Change-Id: Ifd8385290d1cf0d3fc5e8468b2f4ab84e8eff338 Reviewed-on: https://go-review.googlesource.com/c/146782 Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/main.go | 3 + src/cmd/compile/internal/gc/typecheck.go | 96 ++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 059bf5d1fc..49a4e05d99 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -229,6 +229,9 @@ func Main(archInit func(*Arch)) { flag.BoolVar(&flag_race, "race", false, "enable race detector") } objabi.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) + if enableTrace { + flag.BoolVar(&trace, "t", false, "trace type-checking") + } flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity") objabi.Flagcount("w", "debug type checking", &Debug['w']) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 6ee52eae84..be11a9841f 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -12,6 +12,50 @@ import ( "strings" ) +// To enable tracing support (-t flag), set enableTrace to true. +const enableTrace = false + +var trace bool +var traceIndent []byte + +func tracePrint(title string, n *Node) func(np **Node) { + indent := traceIndent + + // guard against nil + var pos, op string + var tc uint8 + if n != nil { + pos = linestr(n.Pos) + op = n.Op.String() + tc = n.Typecheck() + } + + fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc) + traceIndent = append(traceIndent, ". "...) + + return func(np **Node) { + traceIndent = traceIndent[:len(traceIndent)-2] + + // if we have a result, use that + if np != nil { + n = *np + } + + // guard against nil + // use outer pos, op so we don't get empty pos/op if n == nil (nicer output) + var tc uint8 + var typ *types.Type + if n != nil { + pos = linestr(n.Pos) + op = n.Op.String() + tc = n.Typecheck() + typ = n.Type + } + + fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ) + } +} + const ( Etop = 1 << iota // evaluated at statement level Erv // evaluated in value context @@ -31,11 +75,16 @@ const ( var typecheckdefstack []*Node // resolve ONONAME to definition, if any. -func resolve(n *Node) *Node { +func resolve(n *Node) (res *Node) { if n == nil || n.Op != ONONAME { return n } + // only trace if there's work to do + if enableTrace && trace { + defer tracePrint("resolve", n)(&res) + } + if n.Sym.Pkg != localpkg { if inimport { Fatalf("recursive inimport") @@ -150,7 +199,7 @@ var typecheck_tcstack []*Node // typecheck type checks node n. // The result of typecheck MUST be assigned back to n, e.g. // n.Left = typecheck(n.Left, top) -func typecheck(n *Node, top int) *Node { +func typecheck(n *Node, top int) (res *Node) { // cannot type check until all the source has been parsed if !typecheckok { Fatalf("early typecheck") @@ -160,6 +209,11 @@ func typecheck(n *Node, top int) *Node { return nil } + // only trace if there's work to do + if enableTrace && trace { + defer tracePrint("typecheck", n)(&res) + } + lno := setlineno(n) // Skip over parens. @@ -294,7 +348,11 @@ func indexlit(n *Node) *Node { // The result of typecheck1 MUST be assigned back to n, e.g. // n.Left = typecheck1(n.Left, top) -func typecheck1(n *Node, top int) *Node { +func typecheck1(n *Node, top int) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheck1", n)(&res) + } + switch n.Op { case OLITERAL, ONAME, ONONAME, OTYPE: if n.Sym == nil { @@ -2381,7 +2439,11 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost // typecheckMethodExpr checks selector expressions (ODOT) where the // base expression is a type expression (OTYPE). -func typecheckMethodExpr(n *Node) *Node { +func typecheckMethodExpr(n *Node) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheckMethodExpr", n)(&res) + } + t := n.Left.Type // Compute the method set for t. @@ -2924,7 +2986,11 @@ func pushtype(n *Node, t *types.Type) { // The result of typecheckcomplit MUST be assigned back to n, e.g. // n.Left = typecheckcomplit(n.Left) -func typecheckcomplit(n *Node) *Node { +func typecheckcomplit(n *Node) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheckcomplit", n)(&res) + } + lno := lineno defer func() { lineno = lno @@ -3337,6 +3403,10 @@ func samesafeexpr(l *Node, r *Node) bool { // if this assignment is the definition of a var on the left side, // fill in the var's type. func typecheckas(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckas", n)(nil) + } + // delicate little dance. // the definition of n may refer to this assignment // as its definition, in which case it will call typecheckas. @@ -3393,6 +3463,10 @@ func checkassignto(src *types.Type, dst *Node) { } func typecheckas2(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckas2", n)(nil) + } + ls := n.List.Slice() for i1, n1 := range ls { // delicate little dance. @@ -3521,6 +3595,10 @@ out: // type check function definition func typecheckfunc(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckfunc", n)(nil) + } + for _, ln := range n.Func.Dcl { if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) { ln.Name.Decldepth = 1 @@ -3637,6 +3715,10 @@ func copytype(n *Node, t *types.Type) { } func typecheckdeftype(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckdeftype", n)(nil) + } + n.Type.Sym = n.Sym n.SetTypecheck(1) n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype) @@ -3654,6 +3736,10 @@ func typecheckdeftype(n *Node) { } func typecheckdef(n *Node) { + if enableTrace && trace { + defer tracePrint("typecheckdef", n)(nil) + } + lno := setlineno(n) if n.Op == ONONAME { -- 2.50.0