import (
"errors"
"fmt"
- "go/constant"
- "go/token"
"os"
"path/filepath"
"runtime"
"unicode/utf8"
"cmd/compile/internal/base"
- "cmd/compile/internal/dwarfgen"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
noders := make([]*noder, len(filenames))
for i, filename := range filenames {
p := noder{
- err: make(chan syntax.Error),
- trackScopes: base.Flag.Dwarf,
+ err: make(chan syntax.Error),
}
noders[i] = &p
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.
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 {
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.SetInit([]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.SetInit([]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.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.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 wrapname(pos src.XPos, x ir.Node) ir.Node {
// These nodes do not carry line numbers.
// Introduce a wrapper node to give them the correct line.
return x
}
-func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
- return wrapname(p.pos(n), 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)
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 {
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.
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,
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