diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/noder.go')
-rw-r--r-- | src/cmd/compile/internal/noder/noder.go | 349 |
1 files changed, 182 insertions, 167 deletions
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index edd30a1fc1..6a0ee831d7 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -5,6 +5,7 @@ package noder import ( + "errors" "fmt" "go/constant" "go/token" @@ -17,10 +18,12 @@ import ( "unicode/utf8" "cmd/compile/internal/base" + "cmd/compile/internal/dwarfgen" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/compile/internal/types2" "cmd/internal/objabi" "cmd/internal/src" ) @@ -31,6 +34,12 @@ func LoadPackage(filenames []string) { base.Timer.Stop() base.Timer.AddEvent(int64(lines), "lines") + if base.Flag.G != 0 && base.Flag.G < 3 { + // can only parse generic code for now + base.ExitIfErrors() + return + } + // Typecheck. Package() @@ -43,14 +52,13 @@ func LoadPackage(filenames []string) { // Each declaration in every *syntax.File is converted to a syntax tree // and its root represented by *Node is appended to Target.Decls. // Returns the total count of parsed lines. -func ParseFiles(filenames []string) uint { +func ParseFiles(filenames []string) (lines uint) { noders := make([]*noder, 0, len(filenames)) // Limit the number of simultaneously open files. sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) for _, filename := range filenames { p := &noder{ - basemap: make(map[*syntax.PosBase]*src.PosBase), err: make(chan syntax.Error), trackScopes: base.Flag.Dwarf, } @@ -60,7 +68,7 @@ func ParseFiles(filenames []string) uint { sem <- struct{}{} defer func() { <-sem }() defer close(p.err) - base := syntax.NewFileBase(filename) + fbase := syntax.NewFileBase(filename) f, err := os.Open(filename) if err != nil { @@ -69,23 +77,100 @@ func ParseFiles(filenames []string) uint { } defer f.Close() - p.file, _ = syntax.Parse(base, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error + mode := syntax.CheckBranches + if base.Flag.G != 0 { + mode |= syntax.AllowGenerics + } + p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error }(filename) } - var lines uint + // generic noding phase (using new typechecker) + if base.Flag.G != 0 { + // setup and syntax error reporting + nodersmap := make(map[string]*noder) + var files []*syntax.File + for _, p := range noders { + for e := range p.err { + p.errorAt(e.Pos, "%s", e.Msg) + } + + nodersmap[p.file.Pos().RelFilename()] = p + files = append(files, p.file) + lines += p.file.EOF.Line() + + } + if base.SyntaxErrors() != 0 { + base.ErrorExit() + } + + // typechecking + conf := types2.Config{ + InferFromConstraints: true, + IgnoreBranches: true, // parser already checked via syntax.CheckBranches mode + CompilerErrorMessages: true, // use error strings matching existing compiler errors + Error: func(err error) { + terr := err.(types2.Error) + if len(terr.Msg) > 0 && terr.Msg[0] == '\t' { + // types2 reports error clarifications via separate + // error messages which are indented with a tab. + // Ignore them to satisfy tools and tests that expect + // only one error in such cases. + // TODO(gri) Need to adjust error reporting in types2. + return + } + p := nodersmap[terr.Pos.RelFilename()] + base.ErrorfAt(p.makeXPos(terr.Pos), "%s", terr.Msg) + }, + Importer: &gcimports{ + packages: make(map[string]*types2.Package), + }, + Sizes: &gcSizes{}, + } + info := types2.Info{ + Types: make(map[syntax.Expr]types2.TypeAndValue), + Defs: make(map[*syntax.Name]types2.Object), + Uses: make(map[*syntax.Name]types2.Object), + Selections: make(map[*syntax.SelectorExpr]*types2.Selection), + // expand as needed + } + conf.Check(base.Ctxt.Pkgpath, files, &info) + base.ExitIfErrors() + if base.Flag.G < 2 { + return + } + + // noding + for _, p := range noders { + // errors have already been reported + + p.typeInfo = &info + p.node() + lines += p.file.EOF.Line() + p.file = nil // release memory + base.ExitIfErrors() + + // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure. + types.CheckDclstack() + } + + types.LocalPkg.Height = myheight + return + } + + // traditional (non-generic) noding phase for _, p := range noders { for e := range p.err { p.errorAt(e.Pos, "%s", e.Msg) } p.node() - lines += p.file.Lines + lines += p.file.EOF.Line() p.file = nil // release memory - if base.SyntaxErrors() != 0 { base.ErrorExit() } + // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure. types.CheckDclstack() } @@ -95,8 +180,7 @@ func ParseFiles(filenames []string) uint { } types.LocalPkg.Height = myheight - - return lines + return } func Package() { @@ -162,42 +246,6 @@ func Package() { } -// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase. -func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { - // fast path: most likely PosBase hasn't changed - if p.basecache.last == b0 { - return p.basecache.base - } - - b1, ok := p.basemap[b0] - if !ok { - fn := b0.Filename() - if b0.IsFileBase() { - b1 = src.NewFileBase(fn, absFilename(fn)) - } else { - // line directive base - p0 := b0.Pos() - p0b := p0.Base() - if p0b == b0 { - panic("infinite recursion in makeSrcPosBase") - } - p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col()) - b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col()) - } - p.basemap[b0] = b1 - } - - // update cache - p.basecache.last = b0 - p.basecache.base = b1 - - return b1 -} - -func (p *noder) makeXPos(pos syntax.Pos) (_ src.XPos) { - return base.Ctxt.PosTable.XPos(src.MakePos(p.makeSrcPosBase(pos.Base()), pos.Line(), pos.Col())) -} - func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { base.ErrorfAt(p.makeXPos(pos), format, args...) } @@ -213,31 +261,64 @@ func absFilename(name string) string { // noder transforms package syntax's AST into a Node tree. type noder struct { - basemap map[*syntax.PosBase]*src.PosBase - basecache struct { - last *syntax.PosBase - base *src.PosBase - } + posMap file *syntax.File linknames []linkname pragcgobuf [][]string err chan syntax.Error - scope ir.ScopeID importedUnsafe bool importedEmbed bool + trackScopes bool + + funcState *funcState + + // typeInfo provides access to the type information computed by the new + // typechecker. It is only present if -G is set, and all noders point to + // the same types.Info. For now this is a local field, if need be we can + // make it global. + typeInfo *types2.Info +} - // scopeVars is a stack tracking the number of variables declared in the - // current function at the moment each open scope was opened. - trackScopes bool - scopeVars []int +// For now we provide these basic accessors to get to type and object +// information of expression nodes during noding. Eventually we will +// attach this information directly to the syntax tree which should +// simplify access and make it more efficient as well. + +// typ returns the type and value information for the given expression. +func (p *noder) typ(x syntax.Expr) types2.TypeAndValue { + return p.typeInfo.Types[x] +} + +// def returns the object for the given name in its declaration. +func (p *noder) def(x *syntax.Name) types2.Object { + return p.typeInfo.Defs[x] +} + +// use returns the object for the given name outside its declaration. +func (p *noder) use(x *syntax.Name) types2.Object { + return p.typeInfo.Uses[x] +} + +// sel returns the selection information for the given selector expression. +func (p *noder) sel(x *syntax.SelectorExpr) *types2.Selection { + return p.typeInfo.Selections[x] +} + +// funcState tracks all per-function state to make handling nested +// functions easier. +type funcState struct { + // scopeVars is a stack tracking the number of variables declared in + // the current function at the moment each open scope was opened. + scopeVars []int + marker dwarfgen.ScopeMarker lastCloseScopePos syntax.Pos } func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) { - oldScope := p.scope - p.scope = 0 + outerFuncState := p.funcState + p.funcState = new(funcState) typecheck.StartFuncBody(fn) if block != nil { @@ -252,62 +333,34 @@ func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) { } typecheck.FinishFuncBody() - p.scope = oldScope + p.funcState.marker.WriteTo(fn) + p.funcState = outerFuncState } func (p *noder) openScope(pos syntax.Pos) { + fs := p.funcState types.Markdcl() if p.trackScopes { - ir.CurFunc.Parents = append(ir.CurFunc.Parents, p.scope) - p.scopeVars = append(p.scopeVars, len(ir.CurFunc.Dcl)) - p.scope = ir.ScopeID(len(ir.CurFunc.Parents)) - - p.markScope(pos) + fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl)) + fs.marker.Push(p.makeXPos(pos)) } } func (p *noder) closeScope(pos syntax.Pos) { - p.lastCloseScopePos = pos + fs := p.funcState + fs.lastCloseScopePos = pos types.Popdcl() if p.trackScopes { - scopeVars := p.scopeVars[len(p.scopeVars)-1] - p.scopeVars = p.scopeVars[:len(p.scopeVars)-1] + scopeVars := fs.scopeVars[len(fs.scopeVars)-1] + fs.scopeVars = fs.scopeVars[:len(fs.scopeVars)-1] if scopeVars == len(ir.CurFunc.Dcl) { // no variables were declared in this scope, so we can retract it. - - if int(p.scope) != len(ir.CurFunc.Parents) { - base.Fatalf("scope tracking inconsistency, no variables declared but scopes were not retracted") - } - - p.scope = ir.CurFunc.Parents[p.scope-1] - ir.CurFunc.Parents = ir.CurFunc.Parents[:len(ir.CurFunc.Parents)-1] - - nmarks := len(ir.CurFunc.Marks) - ir.CurFunc.Marks[nmarks-1].Scope = p.scope - prevScope := ir.ScopeID(0) - if nmarks >= 2 { - prevScope = ir.CurFunc.Marks[nmarks-2].Scope - } - if ir.CurFunc.Marks[nmarks-1].Scope == prevScope { - ir.CurFunc.Marks = ir.CurFunc.Marks[:nmarks-1] - } - return + fs.marker.Unpush() + } else { + fs.marker.Pop(p.makeXPos(pos)) } - - p.scope = ir.CurFunc.Parents[p.scope-1] - - p.markScope(pos) - } -} - -func (p *noder) markScope(pos syntax.Pos) { - xpos := p.makeXPos(pos) - if i := len(ir.CurFunc.Marks); i > 0 && ir.CurFunc.Marks[i-1].Pos == xpos { - ir.CurFunc.Marks[i-1].Scope = p.scope - } else { - ir.CurFunc.Marks = append(ir.CurFunc.Marks, ir.Mark{Pos: xpos, Scope: p.scope}) } } @@ -316,7 +369,7 @@ func (p *noder) markScope(pos syntax.Pos) { // "if" statements, as their implicit blocks always end at the same // position as an explicit block. func (p *noder) closeAnotherScope() { - p.closeScope(p.lastCloseScopePos) + p.closeScope(p.funcState.lastCloseScopePos) } // linkname records a //go:linkname directive. @@ -396,7 +449,7 @@ func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) { } func (p *noder) importDecl(imp *syntax.ImportDecl) { - if imp.Path.Bad { + if imp.Path == nil || imp.Path.Bad { return // avoid follow-on errors if there was a syntax error } @@ -404,7 +457,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { p.checkUnused(pragma) } - ipkg := importfile(p.basicLit(imp.Path)) + ipkg := importfile(imp) if ipkg == nil { if base.Errors() == 0 { base.Fatalf("phase error in import") @@ -419,11 +472,6 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { p.importedEmbed = true } - if !ipkg.Direct { - typecheck.Target.Imports = append(typecheck.Target.Imports, ipkg) - } - ipkg.Direct = true - var my *types.Sym if imp.LocalPkgName != nil { my = p.name(imp.LocalPkgName) @@ -457,19 +505,8 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node { exprs := p.exprList(decl.Values) if pragma, ok := decl.Pragma.(*pragmas); ok { - if len(pragma.Embeds) > 0 { - if !p.importedEmbed { - // This check can't be done when building the list pragma.Embeds - // because that list is created before the noder starts walking over the file, - // so at that point it hasn't seen the imports. - // We're left to check now, just before applying the //go:embed lines. - for _, e := range pragma.Embeds { - p.errorAt(e.Pos, "//go:embed only allowed in Go files that import \"embed\"") - } - } else { - exprs = varEmbed(p, names, typ, exprs, pragma.Embeds) - } - pragma.Embeds = nil + if err := varEmbed(p.makeXPos, names[0], decl, pragma); err != nil { + p.errorAt(decl.Pos(), "%s", err.Error()) } p.checkUnused(pragma) } @@ -760,7 +797,11 @@ func (p *noder) expr(expr syntax.Expr) ir.Node { case *syntax.Name: return p.mkname(expr) case *syntax.BasicLit: - n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr)) + pos := base.Pos + if expr != syntax.ImplicitOne { // ImplicitOne doesn't have a unique position + pos = p.pos(expr) + } + n := ir.NewBasicLit(pos, p.basicLit(expr)) if expr.Kind == syntax.RuneLit { n.SetType(types.UntypedRune) } @@ -1580,17 +1621,8 @@ func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { return x } -func (p *noder) pos(n syntax.Node) src.XPos { - // TODO(gri): orig.Pos() should always be known - fix package syntax - xpos := base.Pos - if pos := n.Pos(); pos.IsKnown() { - xpos = p.makeXPos(pos) - } - return xpos -} - func (p *noder) setlineno(n syntax.Node) { - if n != nil { + if n != nil && n != syntax.ImplicitOne { base.Pos = p.pos(n) } } @@ -1915,53 +1947,36 @@ func oldname(s *types.Sym) ir.Node { return n } -func varEmbed(p *noder, names []*ir.Name, typ ir.Ntype, exprs []ir.Node, embeds []pragmaEmbed) (newExprs []ir.Node) { - haveEmbed := false - for _, decl := range p.file.DeclList { - imp, ok := decl.(*syntax.ImportDecl) - if !ok { - // imports always come first - break - } - path, _ := strconv.Unquote(imp.Path.Value) - if path == "embed" { - haveEmbed = true - break - } +func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas) error { + if pragma.Embeds == nil { + return nil } - pos := embeds[0].Pos - if !haveEmbed { - p.errorAt(pos, "invalid go:embed: missing import \"embed\"") - return exprs - } + pragmaEmbeds := pragma.Embeds + pragma.Embeds = nil + if base.Flag.Cfg.Embed.Patterns == nil { - p.errorAt(pos, "invalid go:embed: build system did not supply embed configuration") - return exprs + return errors.New("invalid go:embed: build system did not supply embed configuration") } - if len(names) > 1 { - p.errorAt(pos, "go:embed cannot apply to multiple vars") - return exprs + if len(decl.NameList) > 1 { + return errors.New("go:embed cannot apply to multiple vars") } - if len(exprs) > 0 { - p.errorAt(pos, "go:embed cannot apply to var with initializer") - return exprs + if decl.Values != nil { + return errors.New("go:embed cannot apply to var with initializer") } - if typ == nil { - // Should not happen, since len(exprs) == 0 now. - p.errorAt(pos, "go:embed cannot apply to var without type") - return exprs + if decl.Type == nil { + // Should not happen, since Values == nil now. + return errors.New("go:embed cannot apply to var without type") } if typecheck.DeclContext != ir.PEXTERN { - p.errorAt(pos, "go:embed cannot apply to var inside func") - return exprs + return errors.New("go:embed cannot apply to var inside func") } - v := names[0] - typecheck.Target.Embeds = append(typecheck.Target.Embeds, v) - v.Embed = new([]ir.Embed) - for _, e := range embeds { - *v.Embed = append(*v.Embed, ir.Embed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns}) + var embeds []ir.Embed + for _, e := range pragmaEmbeds { + embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns}) } - return exprs + typecheck.Target.Embeds = append(typecheck.Target.Embeds, name) + name.Embed = &embeds + return nil } |