| // 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 noder | |
| import ( | |
| "errors" | |
| "fmt" | |
| "go/constant" | |
| "go/token" | |
| "internal/buildcfg" | |
| "os" | |
| "path/filepath" | |
| "runtime" | |
| "strconv" | |
| "strings" | |
| "unicode" | |
| "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/internal/objabi" | |
| "cmd/internal/src" | |
| ) | |
| func LoadPackage(filenames []string) { | |
| base.Timer.Start("fe", "parse") | |
| // -G=3 and unified expect generics syntax, but -G=0 does not. | |
| supportsGenerics := base.Flag.G != 0 || buildcfg.Experiment.Unified | |
| mode := syntax.CheckBranches | |
| if supportsGenerics { | |
| mode |= syntax.AllowGenerics | |
| } | |
| // Limit the number of simultaneously open files. | |
| sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) | |
| noders := make([]*noder, len(filenames)) | |
| for i, filename := range filenames { | |
| p := noder{ | |
| err: make(chan syntax.Error), | |
| trackScopes: base.Flag.Dwarf, | |
| } | |
| noders[i] = &p | |
| filename := filename | |
| go func() { | |
| sem <- struct{}{} | |
| defer func() { <-sem }() | |
| defer close(p.err) | |
| fbase := syntax.NewFileBase(filename) | |
| f, err := os.Open(filename) | |
| if err != nil { | |
| p.error(syntax.Error{Msg: err.Error()}) | |
| return | |
| } | |
| defer f.Close() | |
| p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error | |
| }() | |
| } | |
| var lines uint | |
| for _, p := range noders { | |
| for e := range p.err { | |
| p.errorAt(e.Pos, "%s", e.Msg) | |
| } | |
| if p.file == nil { | |
| base.ErrorExit() | |
| } | |
| lines += p.file.EOF.Line() | |
| } | |
| base.Timer.AddEvent(int64(lines), "lines") | |
| if base.Debug.Unified != 0 { | |
| unified(noders) | |
| return | |
| } | |
| if base.Flag.G != 0 { | |
| // Use types2 to type-check and possibly generate IR. | |
| check2(noders) | |
| return | |
| } | |
| for _, p := range noders { | |
| p.node() | |
| p.file = nil // release memory | |
| } | |
| if base.SyntaxErrors() != 0 { | |
| base.ErrorExit() | |
| } | |
| types.CheckDclstack() | |
| for _, p := range noders { | |
| p.processPragmas() | |
| } | |
| // Typecheck. | |
| types.LocalPkg.Height = myheight | |
| typecheck.DeclareUniverse() | |
| typecheck.TypecheckAllowed = true | |
| // Process top-level declarations in phases. | |
| // Phase 1: const, type, and names and types of funcs. | |
| // This will gather all the information about types | |
| // and methods but doesn't depend on any of it. | |
| // | |
| // We also defer type alias declarations until phase 2 | |
| // to avoid cycles like #18640. | |
| // TODO(gri) Remove this again once we have a fix for #25838. | |
| // | |
| // Phase 2: Variable assignments. | |
| // To check interface assignments, depends on phase 1. | |
| // Don't use range--typecheck can add closures to Target.Decls. | |
| for phase, name := range []string{"top1", "top2"} { | |
| base.Timer.Start("fe", "typecheck", name) | |
| for i := 0; i < len(typecheck.Target.Decls); i++ { | |
| n := typecheck.Target.Decls[i] | |
| op := n.Op() | |
| // Closure function declarations are typechecked as part of the | |
| // closure expression. | |
| if fn, ok := n.(*ir.Func); ok && fn.OClosure != nil { | |
| continue | |
| } | |
| // We don't actually add ir.ODCL nodes to Target.Decls. Make sure of that. | |
| if op == ir.ODCL { | |
| base.FatalfAt(n.Pos(), "unexpected top declaration: %v", op) | |
| } | |
| // Identify declarations that should be deferred to the second | |
| // iteration. | |
| late := op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() | |
| if late == (phase == 1) { | |
| typecheck.Target.Decls[i] = typecheck.Stmt(n) | |
| } | |
| } | |
| } | |
| // Phase 3: Type check function bodies. | |
| // Don't use range--typecheck can add closures to Target.Decls. | |
| base.Timer.Start("fe", "typecheck", "func") | |
| for i := 0; i < len(typecheck.Target.Decls); i++ { | |
| if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok { | |
| if base.Flag.W > 1 { | |
| s := fmt.Sprintf("\nbefore typecheck %v", fn) | |
| ir.Dump(s, fn) | |
| } | |
| typecheck.FuncBody(fn) | |
| if base.Flag.W > 1 { | |
| s := fmt.Sprintf("\nafter typecheck %v", fn) | |
| ir.Dump(s, fn) | |
| } | |
| } | |
| } | |
| // Phase 4: Check external declarations. | |
| // TODO(mdempsky): This should be handled when type checking their | |
| // corresponding ODCL nodes. | |
| base.Timer.Start("fe", "typecheck", "externdcls") | |
| for i, n := range typecheck.Target.Externs { | |
| if n.Op() == ir.ONAME { | |
| typecheck.Target.Externs[i] = typecheck.Expr(typecheck.Target.Externs[i]) | |
| } | |
| } | |
| // Phase 5: With all user code type-checked, it's now safe to verify map keys. | |
| // With all user code typechecked, it's now safe to verify unused dot imports. | |
| typecheck.CheckMapKeys() | |
| CheckDotImports() | |
| base.ExitIfErrors() | |
| } | |
| func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { | |
| base.ErrorfAt(p.makeXPos(pos), format, args...) | |
| } | |
| // trimFilename returns the "trimmed" filename of b, which is the | |
| // absolute filename after applying -trimpath processing. This | |
| // filename form is suitable for use in object files and export data. | |
| // | |
| // If b's filename has already been trimmed (i.e., because it was read | |
| // in from an imported package's export data), then the filename is | |
| // returned unchanged. | |
| func trimFilename(b *syntax.PosBase) string { | |
| filename := b.Filename() | |
| if !b.Trimmed() { | |
| dir := "" | |
| if b.IsFileBase() { | |
| dir = base.Ctxt.Pathname | |
| } | |
| filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath) | |
| } | |
| return filename | |
| } | |
| // noder transforms package syntax's AST into a Node tree. | |
| type noder struct { | |
| posMap | |
| file *syntax.File | |
| linknames []linkname | |
| pragcgobuf [][]string | |
| err chan syntax.Error | |
| importedUnsafe bool | |
| importedEmbed bool | |
| trackScopes bool | |
| funcState *funcState | |
| } | |
| // 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) { | |
| outerFuncState := p.funcState | |
| p.funcState = new(funcState) | |
| typecheck.StartFuncBody(fn) | |
| if block != nil { | |
| body := p.stmts(block.List) | |
| if body == nil { | |
| body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} | |
| } | |
| fn.Body = body | |
| base.Pos = p.makeXPos(block.Rbrace) | |
| fn.Endlineno = base.Pos | |
| } | |
| typecheck.FinishFuncBody() | |
| p.funcState.marker.WriteTo(fn) | |
| p.funcState = outerFuncState | |
| } | |
| func (p *noder) openScope(pos syntax.Pos) { | |
| fs := p.funcState | |
| types.Markdcl() | |
| if p.trackScopes { | |
| fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl)) | |
| fs.marker.Push(p.makeXPos(pos)) | |
| } | |
| } | |
| func (p *noder) closeScope(pos syntax.Pos) { | |
| fs := p.funcState | |
| fs.lastCloseScopePos = pos | |
| types.Popdcl() | |
| if p.trackScopes { | |
| 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. | |
| fs.marker.Unpush() | |
| } else { | |
| fs.marker.Pop(p.makeXPos(pos)) | |
| } | |
| } | |
| } | |
| // closeAnotherScope is like closeScope, but it reuses the same mark | |
| // position as the last closeScope call. This is useful for "for" and | |
| // "if" statements, as their implicit blocks always end at the same | |
| // position as an explicit block. | |
| func (p *noder) closeAnotherScope() { | |
| p.closeScope(p.funcState.lastCloseScopePos) | |
| } | |
| // linkname records a //go:linkname directive. | |
| type linkname struct { | |
| pos syntax.Pos | |
| local string | |
| remote string | |
| } | |
| func (p *noder) node() { | |
| p.importedUnsafe = false | |
| p.importedEmbed = false | |
| p.setlineno(p.file.PkgName) | |
| mkpackage(p.file.PkgName.Value) | |
| if pragma, ok := p.file.Pragma.(*pragmas); ok { | |
| pragma.Flag &^= ir.GoBuildPragma | |
| p.checkUnused(pragma) | |
| } | |
| typecheck.Target.Decls = append(typecheck.Target.Decls, p.decls(p.file.DeclList)...) | |
| base.Pos = src.NoXPos | |
| clearImports() | |
| } | |
| func (p *noder) processPragmas() { | |
| for _, l := range p.linknames { | |
| if !p.importedUnsafe { | |
| p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") | |
| continue | |
| } | |
| n := ir.AsNode(typecheck.Lookup(l.local).Def) | |
| if n == nil || n.Op() != ir.ONAME { | |
| // TODO(mdempsky): Change to p.errorAt before Go 1.17 release. | |
| // base.WarnfAt(p.makeXPos(l.pos), "//go:linkname must refer to declared function or variable (will be an error in Go 1.17)") | |
| continue | |
| } | |
| if n.Sym().Linkname != "" { | |
| p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local) | |
| continue | |
| } | |
| n.Sym().Linkname = l.remote | |
| } | |
| typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...) | |
| } | |
| func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) { | |
| var cs constState | |
| for _, decl := range decls { | |
| p.setlineno(decl) | |
| switch decl := decl.(type) { | |
| case *syntax.ImportDecl: | |
| p.importDecl(decl) | |
| case *syntax.VarDecl: | |
| l = append(l, p.varDecl(decl)...) | |
| case *syntax.ConstDecl: | |
| l = append(l, p.constDecl(decl, &cs)...) | |
| case *syntax.TypeDecl: | |
| l = append(l, p.typeDecl(decl)) | |
| case *syntax.FuncDecl: | |
| l = append(l, p.funcDecl(decl)) | |
| default: | |
| panic("unhandled Decl") | |
| } | |
| } | |
| return | |
| } | |
| func (p *noder) importDecl(imp *syntax.ImportDecl) { | |
| if imp.Path == nil || imp.Path.Bad { | |
| return // avoid follow-on errors if there was a syntax error | |
| } | |
| if pragma, ok := imp.Pragma.(*pragmas); ok { | |
| p.checkUnused(pragma) | |
| } | |
| ipkg := importfile(imp) | |
| if ipkg == nil { | |
| if base.Errors() == 0 { | |
| base.Fatalf("phase error in import") | |
| } | |
| return | |
| } | |
| if ipkg == types.UnsafePkg { | |
| p.importedUnsafe = true | |
| } | |
| if ipkg.Path == "embed" { | |
| p.importedEmbed = true | |
| } | |
| var my *types.Sym | |
| if imp.LocalPkgName != nil { | |
| my = p.name(imp.LocalPkgName) | |
| } else { | |
| my = typecheck.Lookup(ipkg.Name) | |
| } | |
| pack := ir.NewPkgName(p.pos(imp), my, ipkg) | |
| switch my.Name { | |
| case ".": | |
| importDot(pack) | |
| return | |
| case "init": | |
| base.ErrorfAt(pack.Pos(), "cannot import package as init - init must be a func") | |
| return | |
| case "_": | |
| return | |
| } | |
| if my.Def != nil { | |
| typecheck.Redeclared(pack.Pos(), my, "as imported package name") | |
| } | |
| my.Def = pack | |
| my.Lastlineno = pack.Pos() | |
| my.Block = 1 // at top level | |
| } | |
| func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node { | |
| names := p.declNames(ir.ONAME, decl.NameList) | |
| typ := p.typeExprOrNil(decl.Type) | |
| exprs := p.exprList(decl.Values) | |
| if pragma, ok := decl.Pragma.(*pragmas); ok { | |
| varEmbed(p.makeXPos, names[0], decl, pragma, p.importedEmbed) | |
| p.checkUnused(pragma) | |
| } | |
| var init []ir.Node | |
| p.setlineno(decl) | |
| if len(names) > 1 && len(exprs) == 1 { | |
| as2 := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, exprs) | |
| for _, v := range names { | |
| as2.Lhs.Append(v) | |
| typecheck.Declare(v, typecheck.DeclContext) | |
| v.Ntype = typ | |
| v.Defn = as2 | |
| if ir.CurFunc != nil { | |
| init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) | |
| } | |
| } | |
| return append(init, as2) | |
| } | |
| for i, v := range names { | |
| var e ir.Node | |
| if i < len(exprs) { | |
| e = exprs[i] | |
| } | |
| typecheck.Declare(v, typecheck.DeclContext) | |
| v.Ntype = typ | |
| if ir.CurFunc != nil { | |
| init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) | |
| } | |
| as := ir.NewAssignStmt(base.Pos, v, e) | |
| init = append(init, as) | |
| if e != nil || ir.CurFunc == nil { | |
| v.Defn = as | |
| } | |
| } | |
| if len(exprs) != 0 && len(names) != len(exprs) { | |
| base.Errorf("assignment mismatch: %d variables but %d values", len(names), len(exprs)) | |
| } | |
| return init | |
| } | |
| // constState tracks state between constant specifiers within a | |
| // declaration group. This state is kept separate from noder so nested | |
| // constant declarations are handled correctly (e.g., issue 15550). | |
| type constState struct { | |
| group *syntax.Group | |
| typ ir.Ntype | |
| values syntax.Expr | |
| iota int64 | |
| } | |
| func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node { | |
| if decl.Group == nil || decl.Group != cs.group { | |
| *cs = constState{ | |
| group: decl.Group, | |
| } | |
| } | |
| if pragma, ok := decl.Pragma.(*pragmas); ok { | |
| p.checkUnused(pragma) | |
| } | |
| names := p.declNames(ir.OLITERAL, decl.NameList) | |
| typ := p.typeExprOrNil(decl.Type) | |
| if decl.Values != nil { | |
| cs.typ, cs.values = typ, decl.Values | |
| } else { | |
| if typ != nil { | |
| base.Errorf("const declaration cannot have type without expression") | |
| } | |
| typ = cs.typ | |
| } | |
| values := p.exprList(cs.values) | |
| nn := make([]ir.Node, 0, len(names)) | |
| for i, n := range names { | |
| if i >= len(values) { | |
| base.Errorf("missing value in const declaration") | |
| break | |
| } | |
| v := values[i] | |
| if decl.Values == nil { | |
| ir.Visit(v, func(v ir.Node) { | |
| if ir.HasUniquePos(v) { | |
| v.SetPos(n.Pos()) | |
| } | |
| }) | |
| } | |
| typecheck.Declare(n, typecheck.DeclContext) | |
| n.Ntype = typ | |
| n.Defn = v | |
| n.SetIota(cs.iota) | |
| nn = append(nn, ir.NewDecl(p.pos(decl), ir.ODCLCONST, n)) | |
| } | |
| if len(values) > len(names) { | |
| base.Errorf("extra expression in const declaration") | |
| } | |
| cs.iota++ | |
| return nn | |
| } | |
| func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node { | |
| n := p.declName(ir.OTYPE, decl.Name) | |
| typecheck.Declare(n, typecheck.DeclContext) | |
| // decl.Type may be nil but in that case we got a syntax error during parsing | |
| typ := p.typeExprOrNil(decl.Type) | |
| n.Ntype = typ | |
| n.SetAlias(decl.Alias) | |
| if pragma, ok := decl.Pragma.(*pragmas); ok { | |
| if !decl.Alias { | |
| n.SetPragma(pragma.Flag & typePragmas) | |
| pragma.Flag &^= typePragmas | |
| } | |
| p.checkUnused(pragma) | |
| } | |
| nod := ir.NewDecl(p.pos(decl), ir.ODCLTYPE, n) | |
| if n.Alias() && !types.AllowsGoVersion(types.LocalPkg, 1, 9) { | |
| base.ErrorfAt(nod.Pos(), "type aliases only supported as of -lang=go1.9") | |
| } | |
| return nod | |
| } | |
| func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name { | |
| nodes := make([]*ir.Name, 0, len(names)) | |
| for _, name := range names { | |
| nodes = append(nodes, p.declName(op, name)) | |
| } | |
| return nodes | |
| } | |
| func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name { | |
| return ir.NewDeclNameAt(p.pos(name), op, p.name(name)) | |
| } | |
| func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { | |
| name := p.name(fun.Name) | |
| t := p.signature(fun.Recv, fun.Type) | |
| f := ir.NewFunc(p.pos(fun)) | |
| if fun.Recv == nil { | |
| if name.Name == "init" { | |
| name = renameinit() | |
| if len(t.Params) > 0 || len(t.Results) > 0 { | |
| base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values") | |
| } | |
| typecheck.Target.Inits = append(typecheck.Target.Inits, f) | |
| } | |
| if types.LocalPkg.Name == "main" && name.Name == "main" { | |
| if len(t.Params) > 0 || len(t.Results) > 0 { | |
| base.ErrorfAt(f.Pos(), "func main must have no arguments and no return values") | |
| } | |
| } | |
| } else { | |
| f.Shortname = name | |
| name = ir.BlankNode.Sym() // filled in by tcFunc | |
| } | |
| f.Nname = ir.NewNameAt(p.pos(fun.Name), name) | |
| f.Nname.Func = f | |
| f.Nname.Defn = f | |
| f.Nname.Ntype = t | |
| if pragma, ok := fun.Pragma.(*pragmas); ok { | |
| f.Pragma = pragma.Flag & funcPragmas | |
| if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 { | |
| base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined") | |
| } | |
| pragma.Flag &^= funcPragmas | |
| p.checkUnused(pragma) | |
| } | |
| if fun.Recv == nil { | |
| typecheck.Declare(f.Nname, ir.PFUNC) | |
| } | |
| p.funcBody(f, fun.Body) | |
| if fun.Body != nil { | |
| if f.Pragma&ir.Noescape != 0 { | |
| base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations") | |
| } | |
| } else { | |
| if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") { | |
| // Linknamed functions are allowed to have no body. Hopefully | |
| // the linkname target has a body. See issue 23311. | |
| isLinknamed := false | |
| for _, n := range p.linknames { | |
| if ir.FuncName(f) == n.local { | |
| isLinknamed = true | |
| break | |
| } | |
| } | |
| if !isLinknamed { | |
| base.ErrorfAt(f.Pos(), "missing function body") | |
| } | |
| } | |
| } | |
| return f | |
| } | |
| func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *ir.FuncType { | |
| var rcvr *ir.Field | |
| if recv != nil { | |
| rcvr = p.param(recv, false, false) | |
| } | |
| return ir.NewFuncType(p.pos(typ), rcvr, | |
| p.params(typ.ParamList, true), | |
| p.params(typ.ResultList, false)) | |
| } | |
| func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field { | |
| nodes := make([]*ir.Field, 0, len(params)) | |
| for i, param := range params { | |
| p.setlineno(param) | |
| nodes = append(nodes, p.param(param, dddOk, i+1 == len(params))) | |
| if i > 0 && params[i].Type == params[i-1].Type { | |
| nodes[i].Ntype = nodes[i-1].Ntype | |
| } | |
| } | |
| return nodes | |
| } | |
| func (p *noder) param(param *syntax.Field, dddOk, final bool) *ir.Field { | |
| var name *types.Sym | |
| if param.Name != nil { | |
| name = p.name(param.Name) | |
| } | |
| typ := p.typeExpr(param.Type) | |
| n := ir.NewField(p.pos(param), name, typ, nil) | |
| // rewrite ...T parameter | |
| if typ, ok := typ.(*ir.SliceType); ok && typ.DDD { | |
| if !dddOk { | |
| // We mark these as syntax errors to get automatic elimination | |
| // of multiple such errors per line (see ErrorfAt in subr.go). | |
| base.Errorf("syntax error: cannot use ... in receiver or result parameter list") | |
| } else if !final { | |
| if param.Name == nil { | |
| base.Errorf("syntax error: cannot use ... with non-final parameter") | |
| } else { | |
| p.errorAt(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value) | |
| } | |
| } | |
| typ.DDD = false | |
| n.IsDDD = true | |
| } | |
| return n | |
| } | |
| func (p *noder) exprList(expr syntax.Expr) []ir.Node { | |
| switch expr := expr.(type) { | |
| case nil: | |
| return nil | |
| case *syntax.ListExpr: | |
| return p.exprs(expr.ElemList) | |
| default: | |
| return []ir.Node{p.expr(expr)} | |
| } | |
| } | |
| func (p *noder) exprs(exprs []syntax.Expr) []ir.Node { | |
| nodes := make([]ir.Node, 0, len(exprs)) | |
| for _, expr := range exprs { | |
| nodes = append(nodes, p.expr(expr)) | |
| } | |
| return nodes | |
| } | |
| func (p *noder) expr(expr syntax.Expr) ir.Node { | |
| p.setlineno(expr) | |
| switch expr := expr.(type) { | |
| case nil, *syntax.BadExpr: | |
| return nil | |
| case *syntax.Name: | |
| return p.mkname(expr) | |
| case *syntax.BasicLit: | |
| n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr)) | |
| if expr.Kind == syntax.RuneLit { | |
| n.SetType(types.UntypedRune) | |
| } | |
| n.SetDiag(expr.Bad || n.Val().Kind() == constant.Unknown) // avoid follow-on errors if there was a syntax error | |
| return n | |
| case *syntax.CompositeLit: | |
| n := ir.NewCompLitExpr(p.pos(expr), ir.OCOMPLIT, p.typeExpr(expr.Type), nil) | |
| l := p.exprs(expr.ElemList) | |
| for i, e := range l { | |
| l[i] = p.wrapname(expr.ElemList[i], e) | |
| } | |
| n.List = l | |
| base.Pos = p.makeXPos(expr.Rbrace) | |
| return n | |
| case *syntax.KeyValueExpr: | |
| // use position of expr.Key rather than of expr (which has position of ':') | |
| return ir.NewKeyExpr(p.pos(expr.Key), p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value))) | |
| case *syntax.FuncLit: | |
| return p.funcLit(expr) | |
| case *syntax.ParenExpr: | |
| return ir.NewParenExpr(p.pos(expr), p.expr(expr.X)) | |
| case *syntax.SelectorExpr: | |
| // parser.new_dotname | |
| obj := p.expr(expr.X) | |
| if obj.Op() == ir.OPACK { | |
| pack := obj.(*ir.PkgName) | |
| pack.Used = true | |
| return importName(pack.Pkg.Lookup(expr.Sel.Value)) | |
| } | |
| n := ir.NewSelectorExpr(base.Pos, ir.OXDOT, obj, p.name(expr.Sel)) | |
| n.SetPos(p.pos(expr)) // lineno may have been changed by p.expr(expr.X) | |
| return n | |
| case *syntax.IndexExpr: | |
| return ir.NewIndexExpr(p.pos(expr), p.expr(expr.X), p.expr(expr.Index)) | |
| case *syntax.SliceExpr: | |
| op := ir.OSLICE | |
| if expr.Full { | |
| op = ir.OSLICE3 | |
| } | |
| x := p.expr(expr.X) | |
| var index [3]ir.Node | |
| for i, n := range &expr.Index { | |
| if n != nil { | |
| index[i] = p.expr(n) | |
| } | |
| } | |
| return ir.NewSliceExpr(p.pos(expr), op, x, index[0], index[1], index[2]) | |
| case *syntax.AssertExpr: | |
| return ir.NewTypeAssertExpr(p.pos(expr), p.expr(expr.X), p.typeExpr(expr.Type)) | |
| case *syntax.Operation: | |
| if expr.Op == syntax.Add && expr.Y != nil { | |
| return p.sum(expr) | |
| } | |
| x := p.expr(expr.X) | |
| if expr.Y == nil { | |
| pos, op := p.pos(expr), p.unOp(expr.Op) | |
| switch op { | |
| case ir.OADDR: | |
| return typecheck.NodAddrAt(pos, x) | |
| case ir.ODEREF: | |
| return ir.NewStarExpr(pos, x) | |
| } | |
| return ir.NewUnaryExpr(pos, op, x) | |
| } | |
| pos, op, y := p.pos(expr), p.binOp(expr.Op), p.expr(expr.Y) | |
| switch op { | |
| case ir.OANDAND, ir.OOROR: | |
| return ir.NewLogicalExpr(pos, op, x, y) | |
| } | |
| return ir.NewBinaryExpr(pos, op, x, y) | |
| case *syntax.CallExpr: | |
| n := ir.NewCallExpr(p.pos(expr), ir.OCALL, p.expr(expr.Fun), p.exprs(expr.ArgList)) | |
| n.IsDDD = expr.HasDots | |
| return n | |
| case *syntax.ArrayType: | |
| var len ir.Node | |
| if expr.Len != nil { | |
| len = p.expr(expr.Len) | |
| } | |
| return ir.NewArrayType(p.pos(expr), len, p.typeExpr(expr.Elem)) | |
| case *syntax.SliceType: | |
| return ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) | |
| case *syntax.DotsType: | |
| t := ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) | |
| t.DDD = true | |
| return t | |
| case *syntax.StructType: | |
| return p.structType(expr) | |
| case *syntax.InterfaceType: | |
| return p.interfaceType(expr) | |
| case *syntax.FuncType: | |
| return p.signature(nil, expr) | |
| case *syntax.MapType: | |
| return ir.NewMapType(p.pos(expr), | |
| p.typeExpr(expr.Key), p.typeExpr(expr.Value)) | |
| case *syntax.ChanType: | |
| return ir.NewChanType(p.pos(expr), | |
| p.typeExpr(expr.Elem), p.chanDir(expr.Dir)) | |
| case *syntax.TypeSwitchGuard: | |
| var tag *ir.Ident | |
| if expr.Lhs != nil { | |
| tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs)) | |
| if ir.IsBlank(tag) { | |
| base.Errorf("invalid variable name %v in type switch", tag) | |
| } | |
| } | |
| return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X)) | |
| } | |
| panic("unhandled Expr") | |
| } | |
| // sum efficiently handles very large summation expressions (such as | |
| // in issue #16394). In particular, it avoids left recursion and | |
| // collapses string literals. | |
| func (p *noder) sum(x syntax.Expr) ir.Node { | |
| // While we need to handle long sums with asymptotic | |
| // efficiency, the vast majority of sums are very small: ~95% | |
| // have only 2 or 3 operands, and ~99% of string literals are | |
| // never concatenated. | |
| adds := make([]*syntax.Operation, 0, 2) | |
| for { | |
| add, ok := x.(*syntax.Operation) | |
| if !ok || add.Op != syntax.Add || add.Y == nil { | |
| break | |
| } | |
| adds = append(adds, add) | |
| x = add.X | |
| } | |
| // nstr is the current rightmost string literal in the | |
| // summation (if any), and chunks holds its accumulated | |
| // substrings. | |
| // | |
| // Consider the expression x + "a" + "b" + "c" + y. When we | |
| // reach the string literal "a", we assign nstr to point to | |
| // its corresponding Node and initialize chunks to {"a"}. | |
| // Visiting the subsequent string literals "b" and "c", we | |
| // simply append their values to chunks. Finally, when we | |
| // reach the non-constant operand y, we'll join chunks to form | |
| // "abc" and reassign the "a" string literal's value. | |
| // | |
| // N.B., we need to be careful about named string constants | |
| // (indicated by Sym != nil) because 1) we can't modify their | |
| // value, as doing so would affect other uses of the string | |
| // constant, and 2) they may have types, which we need to | |
| // handle correctly. For now, we avoid these problems by | |
| // treating named string constants the same as non-constant | |
| // operands. | |
| var nstr ir.Node | |
| chunks := make([]string, 0, 1) | |
| n := p.expr(x) | |
| if ir.IsConst(n, constant.String) && n.Sym() == nil { | |
| nstr = n | |
| chunks = append(chunks, ir.StringVal(nstr)) | |
| } | |
| for i := len(adds) - 1; i >= 0; i-- { | |
| add := adds[i] | |
| r := p.expr(add.Y) | |
| if ir.IsConst(r, constant.String) && r.Sym() == nil { | |
| if nstr != nil { | |
| // Collapse r into nstr instead of adding to n. | |
| chunks = append(chunks, ir.StringVal(r)) | |
| continue | |
| } | |
| nstr = r | |
| chunks = append(chunks, ir.StringVal(nstr)) | |
| } else { | |
| if len(chunks) > 1 { | |
| nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) | |
| } | |
| nstr = nil | |
| chunks = chunks[:0] | |
| } | |
| n = ir.NewBinaryExpr(p.pos(add), ir.OADD, n, r) | |
| } | |
| if len(chunks) > 1 { | |
| nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) | |
| } | |
| return n | |
| } | |
| func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype { | |
| // TODO(mdempsky): Be stricter? typecheck should handle errors anyway. | |
| n := p.expr(typ) | |
| if n == nil { | |
| return nil | |
| } | |
| return n.(ir.Ntype) | |
| } | |
| func (p *noder) typeExprOrNil(typ syntax.Expr) ir.Ntype { | |
| if typ != nil { | |
| return p.typeExpr(typ) | |
| } | |
| return nil | |
| } | |
| func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir { | |
| switch dir { | |
| case 0: | |
| return types.Cboth | |
| case syntax.SendOnly: | |
| return types.Csend | |
| case syntax.RecvOnly: | |
| return types.Crecv | |
| } | |
| panic("unhandled ChanDir") | |
| } | |
| func (p *noder) structType(expr *syntax.StructType) ir.Node { | |
| l := make([]*ir.Field, 0, len(expr.FieldList)) | |
| for i, field := range expr.FieldList { | |
| p.setlineno(field) | |
| var n *ir.Field | |
| if field.Name == nil { | |
| n = p.embedded(field.Type) | |
| } else { | |
| n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil) | |
| } | |
| if i > 0 && expr.FieldList[i].Type == expr.FieldList[i-1].Type { | |
| n.Ntype = l[i-1].Ntype | |
| } | |
| if i < len(expr.TagList) && expr.TagList[i] != nil { | |
| n.Note = constant.StringVal(p.basicLit(expr.TagList[i])) | |
| } | |
| l = append(l, n) | |
| } | |
| p.setlineno(expr) | |
| return ir.NewStructType(p.pos(expr), l) | |
| } | |
| func (p *noder) interfaceType(expr *syntax.InterfaceType) ir.Node { | |
| l := make([]*ir.Field, 0, len(expr.MethodList)) | |
| for _, method := range expr.MethodList { | |
| p.setlineno(method) | |
| var n *ir.Field | |
| if method.Name == nil { | |
| n = ir.NewField(p.pos(method), nil, importName(p.packname(method.Type)).(ir.Ntype), nil) | |
| } else { | |
| mname := p.name(method.Name) | |
| if mname.IsBlank() { | |
| base.Errorf("methods must have a unique non-blank name") | |
| continue | |
| } | |
| sig := p.typeExpr(method.Type).(*ir.FuncType) | |
| sig.Recv = fakeRecv() | |
| n = ir.NewField(p.pos(method), mname, sig, nil) | |
| } | |
| l = append(l, n) | |
| } | |
| return ir.NewInterfaceType(p.pos(expr), l) | |
| } | |
| func (p *noder) packname(expr syntax.Expr) *types.Sym { | |
| switch expr := expr.(type) { | |
| case *syntax.Name: | |
| name := p.name(expr) | |
| if n := oldname(name); n.Name() != nil && n.Name().PkgName != nil { | |
| n.Name().PkgName.Used = true | |
| } | |
| return name | |
| case *syntax.SelectorExpr: | |
| name := p.name(expr.X.(*syntax.Name)) | |
| def := ir.AsNode(name.Def) | |
| if def == nil { | |
| base.Errorf("undefined: %v", name) | |
| return name | |
| } | |
| var pkg *types.Pkg | |
| if def.Op() != ir.OPACK { | |
| base.Errorf("%v is not a package", name) | |
| pkg = types.LocalPkg | |
| } else { | |
| def := def.(*ir.PkgName) | |
| def.Used = true | |
| pkg = def.Pkg | |
| } | |
| return pkg.Lookup(expr.Sel.Value) | |
| } | |
| panic(fmt.Sprintf("unexpected packname: %#v", expr)) | |
| } | |
| func (p *noder) embedded(typ syntax.Expr) *ir.Field { | |
| pos := p.pos(syntax.StartPos(typ)) | |
| op, isStar := typ.(*syntax.Operation) | |
| if isStar { | |
| if op.Op != syntax.Mul || op.Y != nil { | |
| panic("unexpected Operation") | |
| } | |
| typ = op.X | |
| } | |
| sym := p.packname(typ) | |
| n := ir.NewField(pos, typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil) | |
| n.Embedded = true | |
| if isStar { | |
| n.Ntype = ir.NewStarExpr(pos, n.Ntype) | |
| } | |
| return n | |
| } | |
| func (p *noder) stmts(stmts []syntax.Stmt) []ir.Node { | |
| return p.stmtsFall(stmts, false) | |
| } | |
| func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node { | |
| var nodes []ir.Node | |
| for i, stmt := range stmts { | |
| s := p.stmtFall(stmt, fallOK && i+1 == len(stmts)) | |
| if s == nil { | |
| } else if s.Op() == ir.OBLOCK && len(s.(*ir.BlockStmt).List) > 0 { | |
| // Inline non-empty block. | |
| // Empty blocks must be preserved for CheckReturn. | |
| nodes = append(nodes, s.(*ir.BlockStmt).List...) | |
| } else { | |
| nodes = append(nodes, s) | |
| } | |
| } | |
| return nodes | |
| } | |
| func (p *noder) stmt(stmt syntax.Stmt) ir.Node { | |
| return p.stmtFall(stmt, false) | |
| } | |
| func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node { | |
| p.setlineno(stmt) | |
| switch stmt := stmt.(type) { | |
| case nil, *syntax.EmptyStmt: | |
| return nil | |
| case *syntax.LabeledStmt: | |
| return p.labeledStmt(stmt, fallOK) | |
| case *syntax.BlockStmt: | |
| l := p.blockStmt(stmt) | |
| if len(l) == 0 { | |
| // TODO(mdempsky): Line number? | |
| return ir.NewBlockStmt(base.Pos, nil) | |
| } | |
| return ir.NewBlockStmt(src.NoXPos, l) | |
| case *syntax.ExprStmt: | |
| return p.wrapname(stmt, p.expr(stmt.X)) | |
| case *syntax.SendStmt: | |
| return ir.NewSendStmt(p.pos(stmt), p.expr(stmt.Chan), p.expr(stmt.Value)) | |
| case *syntax.DeclStmt: | |
| return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList)) | |
| case *syntax.AssignStmt: | |
| if stmt.Rhs == nil { | |
| pos := p.pos(stmt) | |
| n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one)) | |
| n.IncDec = true | |
| return n | |
| } | |
| if stmt.Op != 0 && stmt.Op != syntax.Def { | |
| n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs)) | |
| return n | |
| } | |
| rhs := p.exprList(stmt.Rhs) | |
| if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 { | |
| n := ir.NewAssignListStmt(p.pos(stmt), ir.OAS2, nil, nil) | |
| n.Def = stmt.Op == syntax.Def | |
| n.Lhs = p.assignList(stmt.Lhs, n, n.Def) | |
| n.Rhs = rhs | |
| return n | |
| } | |
| n := ir.NewAssignStmt(p.pos(stmt), nil, nil) | |
| n.Def = stmt.Op == syntax.Def | |
| n.X = p.assignList(stmt.Lhs, n, n.Def)[0] | |
| n.Y = rhs[0] | |
| return n | |
| case *syntax.BranchStmt: | |
| var op ir.Op | |
| switch stmt.Tok { | |
| case syntax.Break: | |
| op = ir.OBREAK | |
| case syntax.Continue: | |
| op = ir.OCONTINUE | |
| case syntax.Fallthrough: | |
| if !fallOK { | |
| base.Errorf("fallthrough statement out of place") | |
| } | |
| op = ir.OFALL | |
| case syntax.Goto: | |
| op = ir.OGOTO | |
| default: | |
| panic("unhandled BranchStmt") | |
| } | |
| var sym *types.Sym | |
| if stmt.Label != nil { | |
| sym = p.name(stmt.Label) | |
| } | |
| return ir.NewBranchStmt(p.pos(stmt), op, sym) | |
| case *syntax.CallStmt: | |
| var op ir.Op | |
| switch stmt.Tok { | |
| case syntax.Defer: | |
| op = ir.ODEFER | |
| case syntax.Go: | |
| op = ir.OGO | |
| default: | |
| panic("unhandled CallStmt") | |
| } | |
| return ir.NewGoDeferStmt(p.pos(stmt), op, p.expr(stmt.Call)) | |
| case *syntax.ReturnStmt: | |
| n := ir.NewReturnStmt(p.pos(stmt), p.exprList(stmt.Results)) | |
| if len(n.Results) == 0 && ir.CurFunc != nil { | |
| for _, ln := range ir.CurFunc.Dcl { | |
| if ln.Class == ir.PPARAM { | |
| continue | |
| } | |
| if ln.Class != ir.PPARAMOUT { | |
| break | |
| } | |
| if ln.Sym().Def != ln { | |
| base.Errorf("%s is shadowed during return", ln.Sym().Name) | |
| } | |
| } | |
| } | |
| return n | |
| case *syntax.IfStmt: | |
| return p.ifStmt(stmt) | |
| case *syntax.ForStmt: | |
| return p.forStmt(stmt) | |
| case *syntax.SwitchStmt: | |
| return p.switchStmt(stmt) | |
| case *syntax.SelectStmt: | |
| return p.selectStmt(stmt) | |
| } | |
| panic("unhandled Stmt") | |
| } | |
| func (p *noder) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node { | |
| if !colas { | |
| return p.exprList(expr) | |
| } | |
| var exprs []syntax.Expr | |
| if list, ok := expr.(*syntax.ListExpr); ok { | |
| exprs = list.ElemList | |
| } else { | |
| exprs = []syntax.Expr{expr} | |
| } | |
| res := make([]ir.Node, len(exprs)) | |
| seen := make(map[*types.Sym]bool, len(exprs)) | |
| newOrErr := false | |
| for i, expr := range exprs { | |
| p.setlineno(expr) | |
| res[i] = ir.BlankNode | |
| name, ok := expr.(*syntax.Name) | |
| if !ok { | |
| p.errorAt(expr.Pos(), "non-name %v on left side of :=", p.expr(expr)) | |
| newOrErr = true | |
| continue | |
| } | |
| sym := p.name(name) | |
| if sym.IsBlank() { | |
| continue | |
| } | |
| if seen[sym] { | |
| p.errorAt(expr.Pos(), "%v repeated on left side of :=", sym) | |
| newOrErr = true | |
| continue | |
| } | |
| seen[sym] = true | |
| if sym.Block == types.Block { | |
| res[i] = oldname(sym) | |
| continue | |
| } | |
| newOrErr = true | |
| n := typecheck.NewName(sym) | |
| typecheck.Declare(n, typecheck.DeclContext) | |
| n.Defn = defn | |
| defn.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n)) | |
| res[i] = n | |
| } | |
| if !newOrErr { | |
| base.ErrorfAt(defn.Pos(), "no new variables on left side of :=") | |
| } | |
| return res | |
| } | |
| func (p *noder) blockStmt(stmt *syntax.BlockStmt) []ir.Node { | |
| p.openScope(stmt.Pos()) | |
| nodes := p.stmts(stmt.List) | |
| p.closeScope(stmt.Rbrace) | |
| return nodes | |
| } | |
| func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node { | |
| p.openScope(stmt.Pos()) | |
| init := p.stmt(stmt.Init) | |
| n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil) | |
| if init != nil { | |
| *n.PtrInit() = []ir.Node{init} | |
| } | |
| if stmt.Else != nil { | |
| e := p.stmt(stmt.Else) | |
| if e.Op() == ir.OBLOCK { | |
| e := e.(*ir.BlockStmt) | |
| n.Else = e.List | |
| } else { | |
| n.Else = []ir.Node{e} | |
| } | |
| } | |
| p.closeAnotherScope() | |
| return n | |
| } | |
| func (p *noder) forStmt(stmt *syntax.ForStmt) ir.Node { | |
| p.openScope(stmt.Pos()) | |
| if r, ok := stmt.Init.(*syntax.RangeClause); ok { | |
| if stmt.Cond != nil || stmt.Post != nil { | |
| panic("unexpected RangeClause") | |
| } | |
| n := ir.NewRangeStmt(p.pos(r), nil, nil, p.expr(r.X), nil) | |
| if r.Lhs != nil { | |
| n.Def = r.Def | |
| lhs := p.assignList(r.Lhs, n, n.Def) | |
| n.Key = lhs[0] | |
| if len(lhs) > 1 { | |
| n.Value = lhs[1] | |
| } | |
| } | |
| n.Body = p.blockStmt(stmt.Body) | |
| p.closeAnotherScope() | |
| return n | |
| } | |
| n := ir.NewForStmt(p.pos(stmt), p.stmt(stmt.Init), p.expr(stmt.Cond), p.stmt(stmt.Post), p.blockStmt(stmt.Body)) | |
| p.closeAnotherScope() | |
| return n | |
| } | |
| func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node { | |
| p.openScope(stmt.Pos()) | |
| init := p.stmt(stmt.Init) | |
| n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil) | |
| if init != nil { | |
| *n.PtrInit() = []ir.Node{init} | |
| } | |
| var tswitch *ir.TypeSwitchGuard | |
| if l := n.Tag; l != nil && l.Op() == ir.OTYPESW { | |
| tswitch = l.(*ir.TypeSwitchGuard) | |
| } | |
| n.Cases = p.caseClauses(stmt.Body, tswitch, stmt.Rbrace) | |
| p.closeScope(stmt.Rbrace) | |
| return n | |
| } | |
| func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *ir.TypeSwitchGuard, rbrace syntax.Pos) []*ir.CaseClause { | |
| nodes := make([]*ir.CaseClause, 0, len(clauses)) | |
| for i, clause := range clauses { | |
| p.setlineno(clause) | |
| if i > 0 { | |
| p.closeScope(clause.Pos()) | |
| } | |
| p.openScope(clause.Pos()) | |
| n := ir.NewCaseStmt(p.pos(clause), p.exprList(clause.Cases), nil) | |
| if tswitch != nil && tswitch.Tag != nil { | |
| nn := typecheck.NewName(tswitch.Tag.Sym()) | |
| typecheck.Declare(nn, typecheck.DeclContext) | |
| n.Var = nn | |
| // keep track of the instances for reporting unused | |
| nn.Defn = tswitch | |
| } | |
| // Trim trailing empty statements. We omit them from | |
| // the Node AST anyway, and it's easier to identify | |
| // out-of-place fallthrough statements without them. | |
| body := clause.Body | |
| for len(body) > 0 { | |
| if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok { | |
| break | |
| } | |
| body = body[:len(body)-1] | |
| } | |
| n.Body = p.stmtsFall(body, true) | |
| if l := len(n.Body); l > 0 && n.Body[l-1].Op() == ir.OFALL { | |
| if tswitch != nil { | |
| base.Errorf("cannot fallthrough in type switch") | |
| } | |
| if i+1 == len(clauses) { | |
| base.Errorf("cannot fallthrough final case in switch") | |
| } | |
| } | |
| nodes = append(nodes, n) | |
| } | |
| if len(clauses) > 0 { | |
| p.closeScope(rbrace) | |
| } | |
| return nodes | |
| } | |
| func (p *noder) selectStmt(stmt *syntax.SelectStmt) ir.Node { | |
| return ir.NewSelectStmt(p.pos(stmt), p.commClauses(stmt.Body, stmt.Rbrace)) | |
| } | |
| func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*ir.CommClause { | |
| nodes := make([]*ir.CommClause, len(clauses)) | |
| for i, clause := range clauses { | |
| p.setlineno(clause) | |
| if i > 0 { | |
| p.closeScope(clause.Pos()) | |
| } | |
| p.openScope(clause.Pos()) | |
| nodes[i] = ir.NewCommStmt(p.pos(clause), p.stmt(clause.Comm), p.stmts(clause.Body)) | |
| } | |
| if len(clauses) > 0 { | |
| p.closeScope(rbrace) | |
| } | |
| return nodes | |
| } | |
| func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node { | |
| sym := p.name(label.Label) | |
| lhs := ir.NewLabelStmt(p.pos(label), sym) | |
| var ls ir.Node | |
| if label.Stmt != nil { // TODO(mdempsky): Should always be present. | |
| ls = p.stmtFall(label.Stmt, fallOK) | |
| // Attach label directly to control statement too. | |
| if ls != nil { | |
| switch ls.Op() { | |
| case ir.OFOR: | |
| ls := ls.(*ir.ForStmt) | |
| ls.Label = sym | |
| case ir.ORANGE: | |
| ls := ls.(*ir.RangeStmt) | |
| ls.Label = sym | |
| case ir.OSWITCH: | |
| ls := ls.(*ir.SwitchStmt) | |
| ls.Label = sym | |
| case ir.OSELECT: | |
| ls := ls.(*ir.SelectStmt) | |
| ls.Label = sym | |
| } | |
| } | |
| } | |
| l := []ir.Node{lhs} | |
| if ls != nil { | |
| if ls.Op() == ir.OBLOCK { | |
| ls := ls.(*ir.BlockStmt) | |
| l = append(l, ls.List...) | |
| } else { | |
| l = append(l, ls) | |
| } | |
| } | |
| return ir.NewBlockStmt(src.NoXPos, l) | |
| } | |
| var unOps = [...]ir.Op{ | |
| syntax.Recv: ir.ORECV, | |
| syntax.Mul: ir.ODEREF, | |
| syntax.And: ir.OADDR, | |
| syntax.Not: ir.ONOT, | |
| syntax.Xor: ir.OBITNOT, | |
| syntax.Add: ir.OPLUS, | |
| syntax.Sub: ir.ONEG, | |
| } | |
| func (p *noder) unOp(op syntax.Operator) ir.Op { | |
| if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 { | |
| panic("invalid Operator") | |
| } | |
| return unOps[op] | |
| } | |
| var binOps = [...]ir.Op{ | |
| syntax.OrOr: ir.OOROR, | |
| syntax.AndAnd: ir.OANDAND, | |
| syntax.Eql: ir.OEQ, | |
| syntax.Neq: ir.ONE, | |
| syntax.Lss: ir.OLT, | |
| syntax.Leq: ir.OLE, | |
| syntax.Gtr: ir.OGT, | |
| syntax.Geq: ir.OGE, | |
| syntax.Add: ir.OADD, | |
| syntax.Sub: ir.OSUB, | |
| syntax.Or: ir.OOR, | |
| syntax.Xor: ir.OXOR, | |
| syntax.Mul: ir.OMUL, | |
| syntax.Div: ir.ODIV, | |
| syntax.Rem: ir.OMOD, | |
| syntax.And: ir.OAND, | |
| syntax.AndNot: ir.OANDNOT, | |
| syntax.Shl: ir.OLSH, | |
| syntax.Shr: ir.ORSH, | |
| } | |
| func (p *noder) binOp(op syntax.Operator) ir.Op { | |
| if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 { | |
| panic("invalid Operator") | |
| } | |
| return binOps[op] | |
| } | |
| // checkLangCompat reports an error if the representation of a numeric | |
| // literal is not compatible with the current language version. | |
| func checkLangCompat(lit *syntax.BasicLit) { | |
| s := lit.Value | |
| if len(s) <= 2 || types.AllowsGoVersion(types.LocalPkg, 1, 13) { | |
| return | |
| } | |
| // len(s) > 2 | |
| if strings.Contains(s, "_") { | |
| base.ErrorfVers("go1.13", "underscores in numeric literals") | |
| return | |
| } | |
| if s[0] != '0' { | |
| return | |
| } | |
| radix := s[1] | |
| if radix == 'b' || radix == 'B' { | |
| base.ErrorfVers("go1.13", "binary literals") | |
| return | |
| } | |
| if radix == 'o' || radix == 'O' { | |
| base.ErrorfVers("go1.13", "0o/0O-style octal literals") | |
| return | |
| } | |
| if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') { | |
| base.ErrorfVers("go1.13", "hexadecimal floating-point literals") | |
| } | |
| } | |
| func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value { | |
| // We don't use the errors of the conversion routines to determine | |
| // if a literal string is valid because the conversion routines may | |
| // accept a wider syntax than the language permits. Rely on lit.Bad | |
| // instead. | |
| if lit.Bad { | |
| return constant.MakeUnknown() | |
| } | |
| switch lit.Kind { | |
| case syntax.IntLit, syntax.FloatLit, syntax.ImagLit: | |
| checkLangCompat(lit) | |
| // The max. mantissa precision for untyped numeric values | |
| // is 512 bits, or 4048 bits for each of the two integer | |
| // parts of a fraction for floating-point numbers that are | |
| // represented accurately in the go/constant package. | |
| // Constant literals that are longer than this many bits | |
| // are not meaningful; and excessively long constants may | |
| // consume a lot of space and time for a useless conversion. | |
| // Cap constant length with a generous upper limit that also | |
| // allows for separators between all digits. | |
| const limit = 10000 | |
| if len(lit.Value) > limit { | |
| p.errorAt(lit.Pos(), "excessively long constant: %s... (%d chars)", lit.Value[:10], len(lit.Value)) | |
| return constant.MakeUnknown() | |
| } | |
| } | |
| v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0) | |
| if v.Kind() == constant.Unknown { | |
| // TODO(mdempsky): Better error message? | |
| p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value) | |
| } | |
| return v | |
| } | |
| var tokenForLitKind = [...]token.Token{ | |
| syntax.IntLit: token.INT, | |
| syntax.RuneLit: token.CHAR, | |
| syntax.FloatLit: token.FLOAT, | |
| syntax.ImagLit: token.IMAG, | |
| syntax.StringLit: token.STRING, | |
| } | |
| func (p *noder) name(name *syntax.Name) *types.Sym { | |
| return typecheck.Lookup(name.Value) | |
| } | |
| func (p *noder) mkname(name *syntax.Name) ir.Node { | |
| // TODO(mdempsky): Set line number? | |
| return mkname(p.name(name)) | |
| } | |
| func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { | |
| // These nodes do not carry line numbers. | |
| // Introduce a wrapper node to give them the correct line. | |
| switch x.Op() { | |
| case ir.OTYPE, ir.OLITERAL: | |
| if x.Sym() == nil { | |
| break | |
| } | |
| fallthrough | |
| case ir.ONAME, ir.ONONAME, ir.OPACK: | |
| p := ir.NewParenExpr(p.pos(n), x) | |
| p.SetImplicit(true) | |
| return p | |
| } | |
| return x | |
| } | |
| func (p *noder) setlineno(n syntax.Node) { | |
| if n != nil { | |
| base.Pos = p.pos(n) | |
| } | |
| } | |
| // error is called concurrently if files are parsed concurrently. | |
| func (p *noder) error(err error) { | |
| p.err <- err.(syntax.Error) | |
| } | |
| // pragmas that are allowed in the std lib, but don't have | |
| // a syntax.Pragma value (see lex.go) associated with them. | |
| var allowedStdPragmas = map[string]bool{ | |
| "go:cgo_export_static": true, | |
| "go:cgo_export_dynamic": true, | |
| "go:cgo_import_static": true, | |
| "go:cgo_import_dynamic": true, | |
| "go:cgo_ldflag": true, | |
| "go:cgo_dynamic_linker": true, | |
| "go:embed": true, | |
| "go:generate": true, | |
| } | |
| // *pragmas is the value stored in a syntax.pragmas during parsing. | |
| type pragmas struct { | |
| Flag ir.PragmaFlag // collected bits | |
| Pos []pragmaPos // position of each individual flag | |
| Embeds []pragmaEmbed | |
| } | |
| type pragmaPos struct { | |
| Flag ir.PragmaFlag | |
| Pos syntax.Pos | |
| } | |
| type pragmaEmbed struct { | |
| Pos syntax.Pos | |
| Patterns []string | |
| } | |
| func (p *noder) checkUnused(pragma *pragmas) { | |
| for _, pos := range pragma.Pos { | |
| if pos.Flag&pragma.Flag != 0 { | |
| p.errorAt(pos.Pos, "misplaced compiler directive") | |
| } | |
| } | |
| if len(pragma.Embeds) > 0 { | |
| for _, e := range pragma.Embeds { | |
| p.errorAt(e.Pos, "misplaced go:embed directive") | |
| } | |
| } | |
| } | |
| func (p *noder) checkUnusedDuringParse(pragma *pragmas) { | |
| for _, pos := range pragma.Pos { | |
| if pos.Flag&pragma.Flag != 0 { | |
| p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) | |
| } | |
| } | |
| if len(pragma.Embeds) > 0 { | |
| for _, e := range pragma.Embeds { | |
| p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) | |
| } | |
| } | |
| } | |
| // pragma is called concurrently if files are parsed concurrently. | |
| func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma { | |
| pragma, _ := old.(*pragmas) | |
| if pragma == nil { | |
| pragma = new(pragmas) | |
| } | |
| if text == "" { | |
| // unused pragma; only called with old != nil. | |
| p.checkUnusedDuringParse(pragma) | |
| return nil | |
| } | |
| if strings.HasPrefix(text, "line ") { | |
| // line directives are handled by syntax package | |
| panic("unreachable") | |
| } | |
| if !blankLine { | |
| // directive must be on line by itself | |
| p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"}) | |
| return pragma | |
| } | |
| switch { | |
| case strings.HasPrefix(text, "go:linkname "): | |
| f := strings.Fields(text) | |
| if !(2 <= len(f) && len(f) <= 3) { | |
| p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"}) | |
| break | |
| } | |
| // The second argument is optional. If omitted, we use | |
| // the default object symbol name for this and | |
| // linkname only serves to mark this symbol as | |
| // something that may be referenced via the object | |
| // symbol name from another package. | |
| var target string | |
| if len(f) == 3 { | |
| target = f[2] | |
| } else if base.Ctxt.Pkgpath != "" { | |
| // Use the default object symbol name if the | |
| // user didn't provide one. | |
| target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1] | |
| } else { | |
| p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"}) | |
| break | |
| } | |
| p.linknames = append(p.linknames, linkname{pos, f[1], target}) | |
| case text == "go:embed", strings.HasPrefix(text, "go:embed "): | |
| args, err := parseGoEmbed(text[len("go:embed"):]) | |
| if err != nil { | |
| p.error(syntax.Error{Pos: pos, Msg: err.Error()}) | |
| } | |
| if len(args) == 0 { | |
| p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) | |
| break | |
| } | |
| pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args}) | |
| case strings.HasPrefix(text, "go:cgo_import_dynamic "): | |
| // This is permitted for general use because Solaris | |
| // code relies on it in golang.org/x/sys/unix and others. | |
| fields := pragmaFields(text) | |
| if len(fields) >= 4 { | |
| lib := strings.Trim(fields[3], `"`) | |
| if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) { | |
| p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)}) | |
| } | |
| p.pragcgo(pos, text) | |
| pragma.Flag |= pragmaFlag("go:cgo_import_dynamic") | |
| break | |
| } | |
| fallthrough | |
| case strings.HasPrefix(text, "go:cgo_"): | |
| // For security, we disallow //go:cgo_* directives other | |
| // than cgo_import_dynamic outside cgo-generated files. | |
| // Exception: they are allowed in the standard library, for runtime and syscall. | |
| if !isCgoGeneratedFile(pos) && !base.Flag.Std { | |
| p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)}) | |
| } | |
| p.pragcgo(pos, text) | |
| fallthrough // because of //go:cgo_unsafe_args | |
| default: | |
| verb := text | |
| if i := strings.Index(text, " "); i >= 0 { | |
| verb = verb[:i] | |
| } | |
| flag := pragmaFlag(verb) | |
| const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec | |
| if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { | |
| p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) | |
| } | |
| if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { | |
| p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) | |
| } | |
| pragma.Flag |= flag | |
| pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos}) | |
| } | |
| return pragma | |
| } | |
| // isCgoGeneratedFile reports whether pos is in a file | |
| // generated by cgo, which is to say a file with name | |
| // beginning with "_cgo_". Such files are allowed to | |
| // contain cgo directives, and for security reasons | |
| // (primarily misuse of linker flags), other files are not. | |
| // See golang.org/issue/23672. | |
| func isCgoGeneratedFile(pos syntax.Pos) bool { | |
| return strings.HasPrefix(filepath.Base(trimFilename(pos.Base())), "_cgo_") | |
| } | |
| // safeArg reports whether arg is a "safe" command-line argument, | |
| // meaning that when it appears in a command-line, it probably | |
| // doesn't have some special meaning other than its own name. | |
| // This is copied from SafeArg in cmd/go/internal/load/pkg.go. | |
| func safeArg(name string) bool { | |
| if name == "" { | |
| return false | |
| } | |
| c := name[0] | |
| return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf | |
| } | |
| func mkname(sym *types.Sym) ir.Node { | |
| n := oldname(sym) | |
| if n.Name() != nil && n.Name().PkgName != nil { | |
| n.Name().PkgName.Used = true | |
| } | |
| return n | |
| } | |
| // parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. | |
| // It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. | |
| // go/build/read.go also processes these strings and contains similar logic. | |
| func parseGoEmbed(args string) ([]string, error) { | |
| var list []string | |
| for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { | |
| var path string | |
| Switch: | |
| switch args[0] { | |
| default: | |
| i := len(args) | |
| for j, c := range args { | |
| if unicode.IsSpace(c) { | |
| i = j | |
| break | |
| } | |
| } | |
| path = args[:i] | |
| args = args[i:] | |
| case '`': | |
| i := strings.Index(args[1:], "`") | |
| if i < 0 { | |
| return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) | |
| } | |
| path = args[1 : 1+i] | |
| args = args[1+i+1:] | |
| case '"': | |
| i := 1 | |
| for ; i < len(args); i++ { | |
| if args[i] == '\\' { | |
| i++ | |
| continue | |
| } | |
| if args[i] == '"' { | |
| q, err := strconv.Unquote(args[:i+1]) | |
| if err != nil { | |
| return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) | |
| } | |
| path = q | |
| args = args[i+1:] | |
| break Switch | |
| } | |
| } | |
| if i >= len(args) { | |
| return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) | |
| } | |
| } | |
| if args != "" { | |
| r, _ := utf8.DecodeRuneInString(args) | |
| if !unicode.IsSpace(r) { | |
| return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) | |
| } | |
| } | |
| list = append(list, path) | |
| } | |
| return list, nil | |
| } | |
| func fakeRecv() *ir.Field { | |
| return ir.NewField(base.Pos, nil, nil, types.FakeRecvType()) | |
| } | |
| func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node { | |
| fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc != nil) | |
| fn.Nname.Ntype = p.typeExpr(expr.Type) | |
| p.funcBody(fn, expr.Body) | |
| ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn) | |
| return fn.OClosure | |
| } | |
| // A function named init is a special case. | |
| // It is called by the initialization before main is run. | |
| // To make it unique within a package and also uncallable, | |
| // the name, normally "pkg.init", is altered to "pkg.init.0". | |
| var renameinitgen int | |
| func renameinit() *types.Sym { | |
| s := typecheck.LookupNum("init.", renameinitgen) | |
| renameinitgen++ | |
| return s | |
| } | |
| // oldname returns the Node that declares symbol s in the current scope. | |
| // If no such Node currently exists, an ONONAME Node is returned instead. | |
| // Automatically creates a new closure variable if the referenced symbol was | |
| // declared in a different (containing) function. | |
| func oldname(s *types.Sym) ir.Node { | |
| if s.Pkg != types.LocalPkg { | |
| return ir.NewIdent(base.Pos, s) | |
| } | |
| n := ir.AsNode(s.Def) | |
| if n == nil { | |
| // Maybe a top-level declaration will come along later to | |
| // define s. resolve will check s.Def again once all input | |
| // source has been processed. | |
| return ir.NewIdent(base.Pos, s) | |
| } | |
| if n, ok := n.(*ir.Name); ok { | |
| // TODO(rsc): If there is an outer variable x and we | |
| // are parsing x := 5 inside the closure, until we get to | |
| // the := it looks like a reference to the outer x so we'll | |
| // make x a closure variable unnecessarily. | |
| return ir.CaptureName(base.Pos, ir.CurFunc, n) | |
| } | |
| return n | |
| } | |
| func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) { | |
| pragmaEmbeds := pragma.Embeds | |
| pragma.Embeds = nil | |
| if len(pragmaEmbeds) == 0 { | |
| return | |
| } | |
| if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil { | |
| base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err) | |
| return | |
| } | |
| var embeds []ir.Embed | |
| for _, e := range pragmaEmbeds { | |
| embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns}) | |
| } | |
| typecheck.Target.Embeds = append(typecheck.Target.Embeds, name) | |
| name.Embed = &embeds | |
| } | |
| func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error { | |
| switch { | |
| case !haveEmbed: | |
| return errors.New("go:embed only allowed in Go files that import \"embed\"") | |
| case len(decl.NameList) > 1: | |
| return errors.New("go:embed cannot apply to multiple vars") | |
| case decl.Values != nil: | |
| return errors.New("go:embed cannot apply to var with initializer") | |
| case decl.Type == nil: | |
| // Should not happen, since Values == nil now. | |
| return errors.New("go:embed cannot apply to var without type") | |
| case withinFunc: | |
| return errors.New("go:embed cannot apply to var inside func") | |
| case !types.AllowsGoVersion(types.LocalPkg, 1, 16): | |
| return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang) | |
| default: | |
| return nil | |
| } | |
| } |