diff options
author | Robert Griesemer <gri@golang.org> | 2016-12-09 17:15:05 -0800 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2017-01-09 22:33:23 +0000 |
commit | 4808fc444307fa683bf3df6d55f9ad1828891a36 (patch) | |
tree | ca3c3cc1b0b52d467091bca2a42f2722154854d9 /src/cmd/compile | |
parent | 2d429f01bd917c42e66e1991eab9c2e33d813d16 (diff) | |
download | go-4808fc444307fa683bf3df6d55f9ad1828891a36.tar.gz go-4808fc444307fa683bf3df6d55f9ad1828891a36.zip |
[dev.inline] cmd/internal/src: replace src.Pos with syntax.Pos
This replaces the src.Pos LineHist-based position tracking with
the syntax.Pos implementation and updates all uses.
The LineHist table is not used anymore - the respective code is still
there but should be removed eventually. CL forthcoming.
Passes toolstash -cmp when comparing to the master repo (with the
exception of a couple of swapped assembly instructions, likely due
to different instruction scheduling because the line-based sorting
has changed; though this is won't affect correctness).
The sizes of various important compiler data structures have increased
significantly (see the various sizes_test.go files); this is probably
the reason for an increase of compilation times (to be addressed). Here
are the results of compilebench -count 5, run on a "quiet" machine (no
apps running besides a terminal):
name old time/op new time/op delta
Template 256ms ± 1% 280ms ±15% +9.54% (p=0.008 n=5+5)
Unicode 132ms ± 1% 132ms ± 1% ~ (p=0.690 n=5+5)
GoTypes 891ms ± 1% 917ms ± 2% +2.88% (p=0.008 n=5+5)
Compiler 3.84s ± 2% 3.99s ± 2% +3.95% (p=0.016 n=5+5)
MakeBash 47.1s ± 1% 47.2s ± 2% ~ (p=0.841 n=5+5)
name old user-ns/op new user-ns/op delta
Template 309M ± 1% 326M ± 2% +5.18% (p=0.008 n=5+5)
Unicode 165M ± 1% 168M ± 4% ~ (p=0.421 n=5+5)
GoTypes 1.14G ± 2% 1.18G ± 1% +3.47% (p=0.008 n=5+5)
Compiler 5.00G ± 1% 5.16G ± 1% +3.12% (p=0.008 n=5+5)
Change-Id: I241c4246cdff627d7ecb95cac23060b38f9775ec
Reviewed-on: https://go-review.googlesource.com/34273
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile')
20 files changed, 150 insertions, 486 deletions
diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index f0f0852c55..88e9ba2b7b 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -650,11 +650,11 @@ var knownFormats = map[string]string{ "cmd/compile/internal/syntax.Node %T": "", "cmd/compile/internal/syntax.Operator %d": "", "cmd/compile/internal/syntax.Operator %s": "", - "cmd/compile/internal/syntax.Pos %s": "", "cmd/compile/internal/syntax.token %d": "", "cmd/compile/internal/syntax.token %q": "", "cmd/compile/internal/syntax.token %s": "", "cmd/internal/obj.As %v": "", + "cmd/internal/src.Pos %s": "", "error %v": "", "float64 %.2f": "", "float64 %.3f": "", diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 50b75aa09f..3c71cea33a 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -189,7 +189,7 @@ func genhash(sym *Sym, t *Type) { fmt.Printf("genhash %v %v\n", sym, t) } - lineno = src.MakePos(1) // less confusing than end of input + lineno = src.MakePos(nil, 1, 0) // less confusing than end of input dclcontext = PEXTERN markdcl() @@ -365,7 +365,7 @@ func geneq(sym *Sym, t *Type) { fmt.Printf("geneq %v %v\n", sym, t) } - lineno = src.MakePos(1) // less confusing than end of input + lineno = src.MakePos(nil, 1, 0) // less confusing than end of input dclcontext = PEXTERN markdcl() diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 438446147a..dbf2d6e166 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -591,7 +591,8 @@ func (p *exporter) pos(n *Node) { func fileLine(n *Node) (file string, line int) { if n != nil { - file, line = Ctxt.LineHist.AbsFileLine(int(n.Pos.Line())) + file = n.Pos.AbsFilename() + line = int(n.Pos.Line()) } return } diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index 5693052fdf..bfb0da5071 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -75,6 +75,9 @@ func anyinit(n []*Node) bool { } func fninit(n []*Node) { + // This code is using the last value of lineno for position information + // (see comment in noder.go, noder.file method, for details). + nf := initfix(n) if !anyinit(nf) { return diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 00dc975f2c..f92387036b 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -12,11 +12,8 @@ import ( "strings" ) -// lexlineno is the line number _after_ the most recently read rune. -// In particular, it's advanced (or rewound) as newlines are read (or unread). -var lexlineno src.Pos - -// lineno is the line number at the start of the most recently lexed token. +// lineno is the source position at the start of the most recently lexed token. +// TODO(gri) rename and eventually remove var lineno src.Pos func isSpace(c rune) bool { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index a3487afb44..aa917c735b 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -186,7 +186,7 @@ func Main() { obj.Flagcount("r", "debug generated wrappers", &Debug['r']) flag.BoolVar(&flag_race, "race", false, "enable race detector") obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) - flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") + flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.BoolVar(&safemode, "u", false, "reject unsafe code") obj.Flagcount("v", "increase debug verbosity", &Debug['v']) obj.Flagcount("w", "debug type checking", &Debug['w']) @@ -300,31 +300,23 @@ func Main() { blockgen = 1 dclcontext = PEXTERN nerrors = 0 - lexlineno = src.MakePos(1) timings.Start("fe", "loadsys") loadsys() timings.Start("fe", "parse") - lexlineno0 := lexlineno + var lines uint for _, infile = range flag.Args() { - linehistpush(infile) block = 1 iota_ = -1000000 imported_unsafe = false - parseFile(infile) + lines += parseFile(infile) if nsyntaxerrors != 0 { errorexit() } - - // Instead of converting EOF into '\n' in getc and count it as an extra line - // for the line history to work, and which then has to be corrected elsewhere, - // just add a line here. - lexlineno = src.MakePos(lexlineno.Line() + 1) - linehistpop() } timings.Stop() - timings.AddEvent(int64(lexlineno.Line()-lexlineno0.Line()), "lines") + timings.AddEvent(int64(lines), "lines") testdclstack() mkpackage(localpkg.Name) // final import not used checks diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index cdfa84aa4c..7376814b43 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -5,25 +5,28 @@ package gc import ( - "cmd/compile/internal/syntax" - "cmd/internal/src" "fmt" "os" "strconv" "strings" "unicode/utf8" + + "cmd/compile/internal/syntax" + "cmd/internal/obj" + "cmd/internal/src" ) -func parseFile(filename string) { - src, err := os.Open(filename) +func parseFile(filename string) uint { + f, err := os.Open(filename) if err != nil { fmt.Println(err) errorexit() } - defer src.Close() + defer f.Close() - p := noder{baseline: lexlineno.Line()} - file, _ := syntax.Parse(filename, src, p.error, p.pragma, 0) // errors are tracked via p.error + base := src.NewFileBase(filename, absFilename(filename)) + var p noder + file, _ := syntax.Parse(base, f, p.error, p.pragma, 0) // errors are tracked via p.error p.file(file) @@ -36,12 +39,19 @@ func parseFile(filename string) { if nsyntaxerrors == 0 { testdclstack() } + + return file.Lines +} + +var pathPrefix string + +func absFilename(name string) string { + return obj.AbsFile(Ctxt.Pathname, name, pathPrefix) } // noder transforms package syntax's AST into a Nod tree. type noder struct { - baseline int32 - linknames []syntax.Pos // tracks //go:linkname positions + linknames []src.Pos // tracks //go:linkname positions } func (p *noder) file(file *syntax.File) { @@ -50,8 +60,16 @@ func (p *noder) file(file *syntax.File) { xtop = append(xtop, p.decls(file.DeclList)...) - lexlineno = src.MakePos(p.baseline + int32(file.Lines) - 1) - lineno = lexlineno + // For compatibility with old code only (comparisons w/ toolstash): + // The old line number tracking simply continued incrementing the + // virtual line number (lexlineno) and using it also for lineno. + // After processing the last function, the lineno was used for the + // line number information of the initialization code (fninit). + // It would be better to use an explicit "<autogenerated>" filename + // for fninit and set lineno to NoPos here. + // TODO(gri) fix this once we switched permanently to the new + // position information. + lineno = src.MakePos(file.Pos().Base(), uint(file.Lines), 0) } func (p *noder) decls(decls []syntax.Decl) (l []*Node) { @@ -231,7 +249,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node { yyerror("can only use //go:noescape with external func implementations") } f.Func.Pragma = pragma - lineno = src.MakePos(p.baseline + int32(fun.EndLine) - 1) + lineno = src.MakePos(fun.Pos().Base(), fun.EndLine, 0) f.Func.Endlineno = lineno funcbody(f) @@ -357,14 +375,14 @@ func (p *noder) expr(expr syntax.Expr) *Node { l[i] = p.wrapname(expr.ElemList[i], e) } n.List.Set(l) - lineno = src.MakePos(p.baseline + int32(expr.EndLine) - 1) + lineno = src.MakePos(expr.Pos().Base(), expr.EndLine, 0) return n case *syntax.KeyValueExpr: return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value))) case *syntax.FuncLit: closurehdr(p.typeExpr(expr.Type)) body := p.stmts(expr.Body) - lineno = src.MakePos(p.baseline + int32(expr.EndLine) - 1) + lineno = src.MakePos(expr.Pos().Base(), expr.EndLine, 0) return p.setlineno(expr, closurebody(body)) case *syntax.ParenExpr: return p.nod(expr, OPAREN, p.expr(expr.X), nil) @@ -986,12 +1004,12 @@ func (p *noder) nod(orig syntax.Node, op Op, left, right *Node) *Node { } func (p *noder) setlineno(src_ syntax.Node, dst *Node) *Node { - l := src_.Pos().Line() - if l == 0 { + pos := src_.Pos() + if !pos.IsKnown() { // TODO(mdempsky): Shouldn't happen. Fix package syntax. return dst } - dst.Pos = src.MakePos(p.baseline + int32(l) - 1) + dst.Pos = pos return dst } @@ -999,48 +1017,24 @@ func (p *noder) lineno(n syntax.Node) { if n == nil { return } - l := n.Pos().Line() - if l == 0 { + pos := n.Pos() + if !pos.IsKnown() { // TODO(mdempsky): Shouldn't happen. Fix package syntax. return } - lineno = src.MakePos(p.baseline + int32(l) - 1) + lineno = pos } func (p *noder) error(err error) { - line := p.baseline - var msg string - if err, ok := err.(syntax.Error); ok { - line += int32(err.Pos.Line()) - 1 - msg = err.Msg - } else { - msg = err.Error() - } - yyerrorl(src.MakePos(line), "%s", msg) + e := err.(syntax.Error) + yyerrorl(e.Pos, "%s", e.Msg) } -func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma { +func (p *noder) pragma(pos src.Pos, text string) syntax.Pragma { switch { case strings.HasPrefix(text, "line "): - // Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails. - i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':') - if i < 0 { - break - } - n, err := strconv.Atoi(text[i+1:]) - if err != nil { - // TODO: make this an error instead? it is almost certainly a bug. - break - } - if n > 1e8 { - p.error(syntax.Error{Pos: pos, Msg: "line number out of range"}) - errorexit() - } - if n <= 0 { - break - } - lexlineno = src.MakePos(p.baseline + int32(pos.Line())) - linehistupdate(text[5:i], n) + // line directives are handled by syntax package + panic("unreachable") case strings.HasPrefix(text, "go:linkname "): // Record line number so we can emit an error later if diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index b86188c314..d8964fd1d0 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -22,14 +22,14 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Func{}, 92, 160}, + {Func{}, 100, 184}, {Name{}, 44, 72}, {Param{}, 24, 48}, - {Node{}, 92, 144}, - {Sym{}, 60, 112}, - {Type{}, 60, 96}, + {Node{}, 96, 160}, + {Sym{}, 64, 128}, + {Type{}, 64, 112}, {MapType{}, 20, 40}, - {ForwardType{}, 16, 32}, + {ForwardType{}, 20, 40}, {FuncType{}, 28, 48}, {StructType{}, 12, 24}, {InterType{}, 4, 8}, diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index a6a8deadc3..4cdc869606 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -282,8 +282,8 @@ func (s *state) label(sym *Sym) *ssaLabel { func (s *state) Logf(msg string, args ...interface{}) { s.config.Logf(msg, args...) } func (s *state) Log() bool { return s.config.Log() } func (s *state) Fatalf(msg string, args ...interface{}) { s.config.Fatalf(s.peekPos(), msg, args...) } -func (s *state) Warnl(line src.Pos, msg string, args ...interface{}) { - s.config.Warnl(line, msg, args...) +func (s *state) Warnl(pos src.Pos, msg string, args ...interface{}) { + s.config.Warnl(pos, msg, args...) } func (s *state) Debug_checknil() bool { return s.config.Debug_checknil() } @@ -4462,8 +4462,11 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { f.Logf("%s\t%s\n", s, p) } if f.Config.HTML != nil { - saved := ptxt.Ctxt.LineHist.PrintFilenameOnly - ptxt.Ctxt.LineHist.PrintFilenameOnly = true + // LineHist is defunct now - this code won't do + // anything. + // TODO: fix this (ideally without a global variable) + // saved := ptxt.Ctxt.LineHist.PrintFilenameOnly + // ptxt.Ctxt.LineHist.PrintFilenameOnly = true var buf bytes.Buffer buf.WriteString("<code>") buf.WriteString("<dl class=\"ssa-gen\">") @@ -4483,7 +4486,7 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) { buf.WriteString("</dl>") buf.WriteString("</code>") f.Config.HTML.WriteColumn("genssa", buf.String()) - ptxt.Ctxt.LineHist.PrintFilenameOnly = saved + // ptxt.Ctxt.LineHist.PrintFilenameOnly = saved } } @@ -4958,8 +4961,8 @@ func (e *ssaExport) CanSSA(t ssa.Type) bool { return canSSAType(t.(*Type)) } -func (e *ssaExport) Line(line src.Pos) string { - return linestr(line) +func (e *ssaExport) Line(pos src.Pos) string { + return linestr(pos) } // Log logs a message from the compiler. @@ -4974,15 +4977,15 @@ func (e *ssaExport) Log() bool { } // Fatal reports a compiler error and exits. -func (e *ssaExport) Fatalf(line src.Pos, msg string, args ...interface{}) { - lineno = line +func (e *ssaExport) Fatalf(pos src.Pos, msg string, args ...interface{}) { + lineno = pos Fatalf(msg, args...) } // Warnl reports a "warning", which is usually flag-triggered // logging output for the benefit of tests. -func (e *ssaExport) Warnl(line src.Pos, fmt_ string, args ...interface{}) { - Warnl(line, fmt_, args...) +func (e *ssaExport) Warnl(pos src.Pos, fmt_ string, args ...interface{}) { + Warnl(pos, fmt_, args...) } func (e *ssaExport) Debug_checknil() bool { diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 6d3d9688d4..44a1ab479b 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -86,49 +86,54 @@ func hcrash() { } } -func linestr(line src.Pos) string { - return Ctxt.Line(int(line.Line())) +func linestr(pos src.Pos) string { + return pos.String() } // lasterror keeps track of the most recently issued error. // It is used to avoid multiple error messages on the same // line. var lasterror struct { - syntax src.Pos // line of last syntax error - other src.Pos // line of last non-syntax error + syntax src.Pos // source position of last syntax error + other src.Pos // source position of last non-syntax error msg string // error message of last non-syntax error } -func yyerrorl(line src.Pos, format string, args ...interface{}) { +// sameline reports whether two positions a, b are on the same line. +func sameline(a, b src.Pos) bool { + return a.Filename() == b.Filename() && a.Line() == b.Line() +} + +func yyerrorl(pos src.Pos, format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) if strings.HasPrefix(msg, "syntax error") { nsyntaxerrors++ // only one syntax error per line, no matter what error - if lasterror.syntax == line { + if sameline(lasterror.syntax, pos) { return } - lasterror.syntax = line + lasterror.syntax = pos } else { // only one of multiple equal non-syntax errors per line // (flusherrors shows only one of them, so we filter them // here as best as we can (they may not appear in order) // so that we don't count them here and exit early, and // then have nothing to show for.) - if lasterror.other == line && lasterror.msg == msg { + if sameline(lasterror.other, pos) && lasterror.msg == msg { return } - lasterror.other = line + lasterror.other = pos lasterror.msg = msg } - adderr(line, "%s", msg) + adderr(pos, "%s", msg) hcrash() nerrors++ if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { flusherrors() - fmt.Printf("%v: too many errors\n", linestr(line)) + fmt.Printf("%v: too many errors\n", linestr(pos)) errorexit() } } @@ -173,34 +178,14 @@ func Fatalf(fmt_ string, args ...interface{}) { errorexit() } +// TODO(gri) rename this function func linehistpragma(file string) { - if Debug['i'] != 0 { - fmt.Printf("pragma %s at line %v\n", file, linestr(lexlineno)) - } + // if Debug['i'] != 0 { + // fmt.Printf("pragma %s at line %v\n", file, linestr(lexlineno)) + // } Ctxt.AddImport(file) } -func linehistpush(file string) { - if Debug['i'] != 0 { - fmt.Printf("import %s at line %v\n", file, linestr(lexlineno)) - } - Ctxt.LineHist.Push(int(lexlineno.Line()), file) -} - -func linehistpop() { - if Debug['i'] != 0 { - fmt.Printf("end of import at line %v\n", linestr(lexlineno)) - } - Ctxt.LineHist.Pop(int(lexlineno.Line())) -} - -func linehistupdate(file string, off int) { - if Debug['i'] != 0 { - fmt.Printf("line %s at line %v\n", file, linestr(lexlineno)) - } - Ctxt.LineHist.Update(int(lexlineno.Line()), file, off) -} - func setlineno(n *Node) src.Pos { lno := lineno if n != nil { @@ -474,9 +459,9 @@ func nodbool(b bool) *Node { // treecopy recursively copies n, with the exception of // ONAME, OLITERAL, OTYPE, and non-iota ONONAME leaves. // Copies of iota ONONAME nodes are assigned the current -// value of iota_. If lineno != 0, it sets the line number -// of newly allocated nodes to lineno. -func treecopy(n *Node, lineno src.Pos) *Node { +// value of iota_. If pos.IsKnown(), it sets the source +// position of newly allocated nodes to pos. +func treecopy(n *Node, pos src.Pos) *Node { if n == nil { return nil } @@ -485,11 +470,11 @@ func treecopy(n *Node, lineno src.Pos) *Node { default: m := *n m.Orig = &m - m.Left = treecopy(n.Left, lineno) - m.Right = treecopy(n.Right, lineno) - m.List.Set(listtreecopy(n.List.Slice(), lineno)) - if lineno.IsKnown() { - m.Pos = lineno + m.Left = treecopy(n.Left, pos) + m.Right = treecopy(n.Right, pos) + m.List.Set(listtreecopy(n.List.Slice(), pos)) + if pos.IsKnown() { + m.Pos = pos } if m.Name != nil && n.Op != ODCLFIELD { Dump("treecopy", n) @@ -504,8 +489,8 @@ func treecopy(n *Node, lineno src.Pos) *Node { // so that all the copies of this const definition // don't have the same iota value. m := *n - if lineno.IsKnown() { - m.Pos = lineno + if pos.IsKnown() { + m.Pos = pos } m.SetIota(iota_) return &m @@ -1707,21 +1692,12 @@ func structargs(tl *Type, mustname bool) []*Node { // method - M func (t T)(), a TFIELD type struct // newnam - the eventual mangled name of this function -var genwrapper_linehistdone int = 0 - func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) { if false && Debug['r'] != 0 { fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam) } - lexlineno = src.MakePos(lexlineno.Line() + 1) - lineno = lexlineno - if genwrapper_linehistdone == 0 { - // All the wrappers can share the same linehist entry. - linehistpush("<autogenerated>") - - genwrapper_linehistdone = 1 - } + lineno = src.MakePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0) dclcontext = PEXTERN markdcl() @@ -1992,10 +1968,10 @@ func Simsimtype(t *Type) EType { return et } -func listtreecopy(l []*Node, lineno src.Pos) []*Node { +func listtreecopy(l []*Node, pos src.Pos) []*Node { var out []*Node for _, n := range l { - out = append(out, treecopy(n, lineno)) + out = append(out, treecopy(n, pos)) } return out } diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go index 7f1d26d370..cfc63738d6 100644 --- a/src/cmd/compile/internal/gc/util.go +++ b/src/cmd/compile/internal/gc/util.go @@ -11,7 +11,7 @@ import ( ) func (n *Node) Line() string { - return Ctxt.LineHist.LineString(int(n.Pos.Line())) + return linestr(n.Pos) } var atExitFuncs []func() diff --git a/src/cmd/compile/internal/ssa/sizeof_test.go b/src/cmd/compile/internal/ssa/sizeof_test.go index 4aab923653..7792318e20 100644 --- a/src/cmd/compile/internal/ssa/sizeof_test.go +++ b/src/cmd/compile/internal/ssa/sizeof_test.go @@ -22,8 +22,8 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Value{}, 68, 112}, - {Block{}, 148, 288}, + {Value{}, 72, 128}, + {Block{}, 152, 304}, } for _, tt := range tests { diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go index 2b20cbdd97..1186193aba 100644 --- a/src/cmd/compile/internal/syntax/dumper_test.go +++ b/src/cmd/compile/internal/syntax/dumper_test.go @@ -14,7 +14,7 @@ func TestDump(t *testing.T) { t.Skip("skipping test in short mode") } - ast, err := ParseFile(*src, nil, nil, 0) + ast, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index adbb4da750..82c6c1907f 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -4,11 +4,13 @@ package syntax +import "cmd/internal/src" + // ---------------------------------------------------------------------------- // Nodes type Node interface { - Pos() Pos + Pos() src.Pos aNode() init(p *parser) } @@ -16,10 +18,10 @@ type Node interface { type node struct { // commented out for now since not yet used // doc *Comment // nil means no comment(s) attached - pos Pos + pos src.Pos } -func (n *node) Pos() Pos { +func (n *node) Pos() src.Pos { return n.pos } diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index eb9c3e4aa5..e70ac3d714 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -5,6 +5,7 @@ package syntax import ( + "cmd/internal/src" "fmt" "io" "strconv" @@ -20,7 +21,7 @@ const trace = false const gcCompat = true type parser struct { - base *PosBase + base *src.PosBase errh ErrorHandler scanner @@ -32,11 +33,11 @@ type parser struct { indent []byte // tracing support } -func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandler) { - p.base = NewFileBase(filename) +func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh PragmaHandler) { + p.base = base p.errh = errh p.scanner.init( - src, + r, // Error and pragma handlers for scanner. // Because the (line, col) positions passed to these // handlers are always at or after the current reading @@ -48,6 +49,7 @@ func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh P func(line, col uint, text string) { if strings.HasPrefix(text, "line ") { p.updateBase(line, col+5, text[5:]) + return } if pragh != nil { p.pragma |= pragh(p.pos_at(line, col), text) @@ -63,6 +65,8 @@ func (p *parser) init(filename string, src io.Reader, errh ErrorHandler, pragh P p.indent = nil } +const lineMax = 1<<24 - 1 // TODO(gri) this limit is defined for src.Pos - fix + func (p *parser) updateBase(line, col uint, text string) { // Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails. i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':') @@ -75,7 +79,7 @@ func (p *parser) updateBase(line, col uint, text string) { p.error_at(p.pos_at(line, col+uint(i+1)), "invalid line number: "+nstr) return } - p.base = NewLinePragmaBase(MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n)) + p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n)) } func (p *parser) got(tok token) bool { @@ -97,12 +101,12 @@ func (p *parser) want(tok token) { // Error handling // pos_at returns the Pos value for (line, col) and the current position base. -func (p *parser) pos_at(line, col uint) Pos { - return MakePos(p.base, line, col) +func (p *parser) pos_at(line, col uint) src.Pos { + return src.MakePos(p.base, line, col) } // error reports an error at the given position. -func (p *parser) error_at(pos Pos, msg string) { +func (p *parser) error_at(pos src.Pos, msg string) { err := Error{pos, msg} if p.first == nil { p.first = err @@ -114,7 +118,7 @@ func (p *parser) error_at(pos Pos, msg string) { } // syntax_error_at reports a syntax error at the given position. -func (p *parser) syntax_error_at(pos Pos, msg string) { +func (p *parser) syntax_error_at(pos src.Pos, msg string) { if trace { defer p.trace("syntax_error (" + msg + ")")() } @@ -159,7 +163,7 @@ func (p *parser) syntax_error_at(pos Pos, msg string) { } // Convenience methods using the current token position. -func (p *parser) pos() Pos { return p.pos_at(p.line, p.col) } +func (p *parser) pos() src.Pos { return p.pos_at(p.line, p.col) } func (p *parser) error(msg string) { p.error_at(p.pos(), msg) } func (p *parser) syntax_error(msg string) { p.syntax_error_at(p.pos(), msg) } diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 23fed3b105..da56168957 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -6,6 +6,7 @@ package syntax import ( "bytes" + "cmd/internal/src" "flag" "fmt" "io/ioutil" @@ -18,11 +19,11 @@ import ( ) var fast = flag.Bool("fast", false, "parse package files in parallel") -var src = flag.String("src", "parser.go", "source file to parse") +var src_ = flag.String("src", "parser.go", "source file to parse") var verify = flag.Bool("verify", false, "verify idempotent printing") func TestParse(t *testing.T) { - _, err := ParseFile(*src, nil, nil, 0) + _, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } @@ -133,7 +134,7 @@ func verifyPrint(filename string, ast1 *File) { panic(err) } - ast2, err := ParseBytes(filename, buf1.Bytes(), nil, nil, 0) + ast2, err := ParseBytes(src.NewFileBase(filename, filename), buf1.Bytes(), nil, nil, 0) if err != nil { panic(err) } @@ -157,7 +158,7 @@ func verifyPrint(filename string, ast1 *File) { } func TestIssue17697(t *testing.T) { - _, err := ParseBytes("", nil, nil, nil, 0) // return with parser error, don't panic + _, err := ParseBytes(nil, nil, nil, nil, 0) // return with parser error, don't panic if err == nil { t.Errorf("no error reported") } @@ -202,7 +203,7 @@ func TestLineDirectives(t *testing.T) { {"//line foo:123\n foo", "syntax error: package statement must be first", "foo", 123, 3}, {"//line foo:123\n//line bar:345\nfoo", "syntax error: package statement must be first", "bar", 345, 0}, } { - _, err := ParseBytes("", []byte(test.src), nil, nil, 0) + _, err := ParseBytes(nil, []byte(test.src), nil, nil, 0) if err == nil { t.Errorf("%s: no error reported", test.src) continue diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go deleted file mode 100644 index 98cdae9327..0000000000 --- a/src/cmd/compile/internal/syntax/pos.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements the encoding of source positions. - -package syntax - -import "strconv" - -// A Pos encodes a source position consisting of a (line, column) number pair -// and a position base. A zero Pos is a ready to use "unknown" position (nil -// position base and zero line number). -// -// The (line, column) values refer to a position in a file independent of any -// position base ("absolute" position). Line numbers start at 1, column values -// start at 0 and are byte offsets from the beginning of the line. -// -// The position base is used to determine the "relative" position, that is the -// filename and line number relative to the position base. If the base refers -// to the current file, there is no difference between absolute and relative -// positions. If it refers to a //line pragma, a relative position is relative -// to that pragma. A position base in turn contains the position at which it -// was introduced in the current file. -type Pos struct { - base *PosBase - lico -} - -// NoPos is a valid unknown position. -var NoPos Pos - -// MakePos creates a new Pos value with the given base, and (file-absolute) -// line and column. -func MakePos(base *PosBase, line, col uint) Pos { - return Pos{base, makeLico(line, col)} -} - -// IsKnown reports whether the position p is known. -func (p Pos) IsKnown() bool { - return p.base != nil || p.Line() != 0 -} - -// Before reports whether the position p comes before q in the source. -// For positions in different files, ordering is by filename. -func (p Pos) Before(q Pos) bool { - n, m := p.Filename(), q.Filename() - return n < m || n == m && p.lico < q.lico -} - -// After reports whether the position p comes after q in the source. -// For positions in different files, ordering is by filename. -func (p Pos) After(q Pos) bool { - n, m := p.Filename(), q.Filename() - return n > m || n == m && p.lico > q.lico -} - -// Filename returns the name of the actual file containing this position. -func (p Pos) Filename() string { return p.base.Pos().RelFilename() } - -// Base returns the position base. -func (p Pos) Base() *PosBase { return p.base } - -// RelFilename returns the filename recorded with the position's base. -func (p Pos) RelFilename() string { return p.base.Filename() } - -// RelLine returns the line number relative to the positions's base. -func (p Pos) RelLine() uint { b := p.base; return b.Line() + p.Line() - b.Pos().Line() } - -func (p Pos) String() string { - b := p.base - - if b == b.Pos().base { - // base is file base (incl. nil) - return posString(b.Filename(), p.Line(), p.Col()) - } - - // base is relative - return posString(b.Filename(), p.RelLine(), p.Col()) + "[" + b.Pos().String() + "]" -} - -// posString formats a (filename, line, col) tuple as a printable position. -func posString(filename string, line, col uint) string { - s := filename + ":" + strconv.FormatUint(uint64(line), 10) - // col == colMax is interpreted as unknown column value - if col < colMax { - s += ":" + strconv.FormatUint(uint64(col), 10) - } - return s -} - -// ---------------------------------------------------------------------------- -// PosBase - -// A PosBase encodes a filename and base line number. -// Typically, each file and line pragma introduce a PosBase. -// A nil *PosBase is a ready to use file PosBase for an unnamed -// file with line numbers starting at 1. -type PosBase struct { - pos Pos - filename string - line uint -} - -// NewFileBase returns a new *PosBase for a file with the given filename. -func NewFileBase(filename string) *PosBase { - if filename != "" { - base := &PosBase{filename: filename} - base.pos = MakePos(base, 0, 0) - return base - } - return nil -} - -// NewLinePragmaBase returns a new *PosBase for a line pragma of the form -// //line filename:line -// at position pos. -func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase { - return &PosBase{pos, filename, line - 1} -} - -var noPos Pos - -// Pos returns the position at which base is located. -// If b == nil, the result is the zero position. -func (b *PosBase) Pos() *Pos { - if b != nil { - return &b.pos - } - return &noPos -} - -// Filename returns the filename recorded with the base. -// If b == nil, the result is the empty string. -func (b *PosBase) Filename() string { - if b != nil { - return b.filename - } - return "" -} - -// Line returns the line number recorded with the base. -// If b == nil, the result is 0. -func (b *PosBase) Line() uint { - if b != nil { - return b.line - } - return 0 -} - -// ---------------------------------------------------------------------------- -// lico - -// A lico is a compact encoding of a LIne and COlumn number. -type lico uint32 - -// Layout constants: 24 bits for line, 8 bits for column. -// (If this is too tight, we can either make lico 64b wide, -// or we can introduce a tiered encoding where we remove column -// information as line numbers grow bigger; similar to what gcc -// does.) -const ( - lineBits, lineMax = 24, 1<<lineBits - 1 - colBits, colMax = 32 - lineBits, 1<<colBits - 1 -) - -func makeLico(line, col uint) lico { - if line > lineMax { - // cannot represent line, use max. line so we have some information - line = lineMax - } - if col > colMax { - // cannot represent column, use max. column so we have some information - col = colMax - } - return lico(line<<colBits | col) -} - -func (x lico) Line() uint { return uint(x) >> colBits } -func (x lico) Col() uint { return uint(x) & colMax } diff --git a/src/cmd/compile/internal/syntax/pos_test.go b/src/cmd/compile/internal/syntax/pos_test.go deleted file mode 100644 index bf2a0c1dfa..0000000000 --- a/src/cmd/compile/internal/syntax/pos_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package syntax - -import ( - "fmt" - "testing" -) - -func TestPos(t *testing.T) { - f0 := NewFileBase("") - f1 := NewFileBase("f1") - f2 := NewLinePragmaBase(Pos{}, "f2", 10) - f3 := NewLinePragmaBase(MakePos(f1, 10, 1), "f3", 100) - f4 := NewLinePragmaBase(MakePos(f3, 10, 1), "f4", 100) - - for _, test := range []struct { - pos Pos - string string - - // absolute info - filename string - line, col uint - - // relative info - relFilename string - relLine uint - }{ - {Pos{}, ":0:0", "", 0, 0, "", 0}, - {MakePos(nil, 2, 3), ":2:3", "", 2, 3, "", 2}, - {MakePos(f0, 2, 3), ":2:3", "", 2, 3, "", 2}, - {MakePos(f1, 1, 1), "f1:1:1", "f1", 1, 1, "f1", 1}, - {MakePos(f2, 7, 10), "f2:16:10[:0:0]", "", 7, 10, "f2", 16}, - {MakePos(f3, 12, 7), "f3:101:7[f1:10:1]", "f1", 12, 7, "f3", 101}, - {MakePos(f4, 25, 1), "f4:114:1[f3:99:1[f1:10:1]]", "f3", 25, 1, "f4", 114}, // doesn't occur in Go code - } { - pos := test.pos - if got := pos.String(); got != test.string { - t.Errorf("%s: got %q", test.string, got) - } - - // absolute info - if got := pos.Filename(); got != test.filename { - t.Errorf("%s: got filename %q; want %q", test.string, got, test.filename) - } - if got := pos.Line(); got != test.line { - t.Errorf("%s: got line %d; want %d", test.string, got, test.line) - } - if got := pos.Col(); got != test.col { - t.Errorf("%s: got col %d; want %d", test.string, got, test.col) - } - - // relative info - if got := pos.RelFilename(); got != test.relFilename { - t.Errorf("%s: got relFilename %q; want %q", test.string, got, test.relFilename) - } - if got := pos.RelLine(); got != test.relLine { - t.Errorf("%s: got relLine %d; want %d", test.string, got, test.relLine) - } - } -} - -func TestPredicates(t *testing.T) { - b1 := NewFileBase("b1") - b2 := NewFileBase("b2") - for _, test := range []struct { - p, q Pos - known, before, after bool - }{ - {NoPos, NoPos, false, false, false}, - {NoPos, MakePos(nil, 1, 0), false, true, false}, - {MakePos(b1, 0, 0), NoPos, true, false, true}, - {MakePos(nil, 1, 0), NoPos, true, false, true}, - - {MakePos(nil, 1, 1), MakePos(nil, 1, 1), true, false, false}, - {MakePos(nil, 1, 1), MakePos(nil, 1, 2), true, true, false}, - {MakePos(nil, 1, 2), MakePos(nil, 1, 1), true, false, true}, - {MakePos(nil, 123, 1), MakePos(nil, 1, 123), true, false, true}, - - {MakePos(b1, 1, 1), MakePos(b1, 1, 1), true, false, false}, - {MakePos(b1, 1, 1), MakePos(b1, 1, 2), true, true, false}, - {MakePos(b1, 1, 2), MakePos(b1, 1, 1), true, false, true}, - {MakePos(b1, 123, 1), MakePos(b1, 1, 123), true, false, true}, - - {MakePos(b1, 1, 1), MakePos(b2, 1, 1), true, true, false}, - {MakePos(b1, 1, 1), MakePos(b2, 1, 2), true, true, false}, - {MakePos(b1, 1, 2), MakePos(b2, 1, 1), true, true, false}, - {MakePos(b1, 123, 1), MakePos(b2, 1, 123), true, true, false}, - - // special case: unknown column (column too large to represent) - {MakePos(nil, 1, colMax+10), MakePos(nil, 1, colMax+20), true, false, false}, - } { - if got := test.p.IsKnown(); got != test.known { - t.Errorf("%s known: got %v; want %v", test.p, got, test.known) - } - if got := test.p.Before(test.q); got != test.before { - t.Errorf("%s < %s: got %v; want %v", test.p, test.q, got, test.before) - } - if got := test.p.After(test.q); got != test.after { - t.Errorf("%s > %s: got %v; want %v", test.p, test.q, got, test.after) - } - } -} - -func TestLico(t *testing.T) { - for _, test := range []struct { - x lico - string string - line, col uint - }{ - {0, ":0:0", 0, 0}, - {makeLico(0, 0), ":0:0", 0, 0}, - {makeLico(0, 1), ":0:1", 0, 1}, - {makeLico(1, 0), ":1:0", 1, 0}, - {makeLico(1, 1), ":1:1", 1, 1}, - {makeLico(2, 3), ":2:3", 2, 3}, - {makeLico(lineMax, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1}, - {makeLico(lineMax+1, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1}, // line too large, stick with max. line - {makeLico(1, colMax), ":1", 1, colMax}, - {makeLico(1, colMax+1), ":1", 1, 0}, // column too large - {makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0}, - } { - x := test.x - if got := posString("", x.Line(), x.Col()); got != test.string { - t.Errorf("%s: got %q", test.string, got) - } - } -} diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 5c0fc776a1..dc9a32b6d3 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -15,7 +15,7 @@ func TestPrint(t *testing.T) { t.Skip("skipping test in short mode") } - ast, err := ParseFile(*src, nil, nil, 0) + ast, err := ParseFile(*src_, nil, nil, 0) if err != nil { t.Fatal(err) } diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go index 4585defb8f..db2bcb4a0c 100644 --- a/src/cmd/compile/internal/syntax/syntax.go +++ b/src/cmd/compile/internal/syntax/syntax.go @@ -5,6 +5,7 @@ package syntax import ( + "cmd/internal/src" "fmt" "io" "os" @@ -15,7 +16,7 @@ type Mode uint // Error describes a syntax error. Error implements the error interface. type Error struct { - Pos Pos + Pos src.Pos Msg string } @@ -36,11 +37,11 @@ type Pragma uint16 // A PragmaHandler is used to process //line and //go: directives as // they're scanned. The returned Pragma value will be unioned into the // next FuncDecl node. -type PragmaHandler func(pos Pos, text string) Pragma +type PragmaHandler func(pos src.Pos, text string) Pragma // Parse parses a single Go source file from src and returns the corresponding // syntax tree. If there are errors, Parse will return the first error found. -// The filename is only used for position information. +// The base argument is only used for position information. // // If errh != nil, it is called with each error encountered, and Parse will // process as much source as possible. If errh is nil, Parse will terminate @@ -49,7 +50,7 @@ type PragmaHandler func(pos Pos, text string) Pragma // If a PragmaHandler is provided, it is called with each pragma encountered. // // The Mode argument is currently ignored. -func Parse(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { +func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { defer func() { if p := recover(); p != nil { if err, ok := p.(Error); ok { @@ -61,14 +62,14 @@ func Parse(filename string, src io.Reader, errh ErrorHandler, pragh PragmaHandle }() var p parser - p.init(filename, src, errh, pragh) + p.init(base, src, errh, pragh) p.next() return p.file(), p.first } // ParseBytes behaves like Parse but it reads the source from the []byte slice provided. -func ParseBytes(filename string, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { - return Parse(filename, &bytesReader{src}, errh, pragh, mode) +func ParseBytes(base *src.PosBase, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { + return Parse(base, &bytesReader{src}, errh, pragh, mode) } type bytesReader struct { @@ -86,13 +87,13 @@ func (r *bytesReader) Read(p []byte) (int, error) { // ParseFile behaves like Parse but it reads the source from the named file. func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { - src, err := os.Open(filename) + f, err := os.Open(filename) if err != nil { if errh != nil { errh(err) } return nil, err } - defer src.Close() - return Parse(filename, src, errh, pragh, mode) + defer f.Close() + return Parse(src.NewFileBase(filename, filename), f, errh, pragh, mode) } |