go/src/cmd/compile/internal/gc/noder.go

1168 lines
26 KiB
Go
Raw Normal View History

// 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 gc
import (
"fmt"
"strconv"
"strings"
"unicode/utf8"
"cmd/compile/internal/syntax"
)
func parseFile(filename string) {
p := noder{baseline: lexlineno}
file, err := syntax.ReadFile(filename, p.error, p.pragma, 0)
if err != nil {
Fatalf("syntax.ReadFile %s: %v", filename, err)
}
p.file(file)
if !imported_unsafe {
for _, x := range p.linknames {
p.error(0, x, "//go:linkname only allowed in Go files that import \"unsafe\"")
}
}
if nsyntaxerrors == 0 {
testdclstack()
}
}
// noder transforms package syntax's AST into a Nod tree.
type noder struct {
baseline int32
linknames []int // tracks //go:linkname lines
}
func (p *noder) file(file *syntax.File) {
p.lineno(file.PkgName)
mkpackage(file.PkgName.Value)
xtop = append(xtop, p.decls(file.DeclList)...)
lexlineno = p.baseline + int32(file.Lines) - 1
lineno = lexlineno
}
func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
var lastConstGroup *syntax.Group
var lastConstRHS []*Node
var iotaVal int64
for _, decl := range decls {
p.lineno(decl)
switch decl := decl.(type) {
case *syntax.ImportDecl:
p.importDecl(decl)
case *syntax.AliasDecl:
p.aliasDecl(decl)
case *syntax.VarDecl:
l = append(l, p.varDecl(decl)...)
case *syntax.ConstDecl:
// Tricky to handle golang.org/issue/15550 correctly.
prevIota := iota_
if decl.Group == nil || decl.Group != lastConstGroup {
iotaVal = 0
lastConstRHS = nil
}
iota_ = iotaVal
lastconst = lastConstRHS
l = append(l, p.constDecl(decl)...)
lastConstRHS = lastconst
lastconst = nil
iota_ = prevIota
iotaVal++
lastConstGroup = decl.Group
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) {
val := p.basicLit(imp.Path)
importfile(&val, nil)
ipkg := importpkg
importpkg = nil
if ipkg == nil {
if nerrors == 0 {
Fatalf("phase error in import")
}
return
}
ipkg.Direct = true
var my *Sym
if imp.LocalPkgName != nil {
my = p.name(imp.LocalPkgName)
} else {
my = lookup(ipkg.Name)
}
pack := p.nod(imp, OPACK, nil, nil)
pack.Sym = my
pack.Name.Pkg = ipkg
if my.Name == "." {
importdot(ipkg, pack)
return
}
if my.Name == "init" {
yyerrorl(pack.Lineno, "cannot import package as init - init must be a func")
return
}
if my.Name == "_" {
return
}
if my.Def != nil {
lineno = pack.Lineno
redeclare(my, "as imported package name")
}
my.Def = pack
my.Lastlineno = pack.Lineno
my.Block = 1 // at top level
}
func (p *noder) aliasDecl(decl *syntax.AliasDecl) {
// Because alias declarations must refer to imported entities
// which are already set up, we can do all checks right here.
// We won't know anything about entities that have not been
// declared yet, but since they cannot have been imported, we
// know there's an error and we don't care about the details.
// The original entity must be denoted by a qualified identifier.
// (The parser doesn't make this restriction to be more error-
// tolerant.)
qident, ok := decl.Orig.(*syntax.SelectorExpr)
if !ok {
// TODO(gri) This prints a dot-imported object with qualification
// (confusing error). Fix this.
yyerror("invalid alias: %v is not a package-qualified identifier", p.expr(decl.Orig))
return
}
pkg := p.expr(qident.X)
if pkg.Op != OPACK {
yyerror("invalid alias: %v is not a package", pkg)
return
}
pkg.Used = true
// Resolve original entity
orig := oldname(restrictlookup(qident.Sel.Value, pkg.Name.Pkg))
if orig.Sym.Flags&SymAlias != 0 {
Fatalf("original %v marked as alias", orig.Sym)
}
// An alias declaration must not refer to package unsafe.
if orig.Sym.Pkg == unsafepkg {
yyerror("invalid alias: %v refers to package unsafe (%v)", decl.Name.Value, orig)
return
}
// The aliased entity must be from a matching constant, type, variable,
// or function declaration, respectively.
var what string
switch decl.Tok {
case syntax.Const:
if orig.Op != OLITERAL {
what = "constant"
}
case syntax.Type:
if orig.Op != OTYPE {
what = "type"
}
case syntax.Var:
if orig.Op != ONAME || orig.Class != PEXTERN {
what = "variable"
}
case syntax.Func:
if orig.Op != ONAME || orig.Class != PFUNC {
what = "function"
}
default:
Fatalf("unexpected token: %s", decl.Tok)
}
if what != "" {
yyerror("invalid alias: %v is not a %s", orig, what)
return
}
// handle special cases
switch decl.Name.Value {
case "_":
return // don't declare blank aliases
case "init":
yyerror("cannot declare init - must be non-alias function declaration")
return
}
// declare alias
// (this is similar to handling dot imports)
asym := p.name(decl.Name)
if asym.Def != nil {
redeclare(asym, "in alias declaration")
return
}
asym.Flags |= SymAlias
asym.Def = orig
asym.Block = block
asym.Lastlineno = lineno
if exportname(asym.Name) {
// TODO(gri) newname(asym) is only needed to satisfy exportsym
// (and indirectly, exportlist). We should be able to just
// collect the Syms, eventually.
exportsym(newname(asym))
}
}
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
names := p.declNames(decl.NameList)
var typ *Node
if decl.Type != nil {
typ = p.typeExpr(decl.Type)
}
var exprs []*Node
if decl.Values != nil {
exprs = p.exprList(decl.Values)
}
p.lineno(decl)
return variter(names, typ, exprs)
}
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
names := p.declNames(decl.NameList)
var typ *Node
if decl.Type != nil {
typ = p.typeExpr(decl.Type)
}
var exprs []*Node
if decl.Values != nil {
exprs = p.exprList(decl.Values)
}
return constiter(names, typ, exprs)
}
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
name := typedcl0(p.name(decl.Name))
cmd/compile: add go:notinheap type pragma This adds a //go:notinheap pragma for declarations of types that must not be heap allocated. We ensure these rules by disallowing new(T), make([]T), append([]T), or implicit allocation of T, by disallowing conversions to notinheap types, and by propagating notinheap to any struct or array that contains notinheap elements. The utility of this pragma is that we can eliminate write barriers for writes to pointers to go:notinheap types, since the write barrier is guaranteed to be a no-op. This will let us mark several scheduler and memory allocator structures as go:notinheap, which will let us disallow write barriers in the scheduler and memory allocator much more thoroughly and also eliminate some problematic hybrid write barriers. This also makes go:nowritebarrierrec and go:yeswritebarrierrec much more powerful. Currently we use go:nowritebarrier all over the place, but it's almost never what you actually want: when write barriers are illegal, they're typically illegal for a whole dynamic scope. Partly this is because go:nowritebarrier has been around longer, but it's also because go:nowritebarrierrec couldn't be used in situations that had no-op write barriers or where some nested scope did allow write barriers. go:notinheap eliminates many no-op write barriers and go:yeswritebarrierrec makes it possible to opt back in to write barriers, so these two changes will let us use go:nowritebarrierrec far more liberally. This updates #13386, which is about controlling pointers from non-GC'd memory to GC'd memory. That would require some additional pragma (or pragmas), but could build on this pragma. Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd Reviewed-on: https://go-review.googlesource.com/30939 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 22:53:27 -04:00
name.Name.Param.Pragma = Pragma(decl.Pragma)
var typ *Node
if decl.Type != nil {
typ = p.typeExpr(decl.Type)
}
return typedcl1(name, typ, true)
}
func (p *noder) declNames(names []*syntax.Name) []*Node {
var nodes []*Node
for _, name := range names {
nodes = append(nodes, p.declName(name))
}
return nodes
}
func (p *noder) declName(name *syntax.Name) *Node {
// TODO(mdempsky): Set lineno?
return dclname(p.name(name))
}
func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
f := p.funcHeader(fun)
if f == nil {
return nil
}
var body []*Node
if fun.Body != nil {
body = p.stmts(fun.Body)
if body == nil {
body = []*Node{p.nod(fun, OEMPTY, nil, nil)}
}
}
pragma := Pragma(fun.Pragma)
f.Nbody.Set(body)
f.Noescape = pragma&Noescape != 0
if f.Noescape && len(body) != 0 {
yyerror("can only use //go:noescape with external func implementations")
}
f.Func.Pragma = pragma
lineno = p.baseline + int32(fun.EndLine) - 1
f.Func.Endlineno = lineno
funcbody(f)
return f
}
func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
name := p.name(fun.Name)
t := p.signature(fun.Recv, fun.Type)
f := p.nod(fun, ODCLFUNC, nil, nil)
if fun.Recv == nil {
// FunctionName Signature
if name.Name == "init" {
name = renameinit()
if t.List.Len() > 0 || t.Rlist.Len() > 0 {
yyerror("func init must have no arguments and no return values")
}
}
if localpkg.Name == "main" && name.Name == "main" {
if t.List.Len() > 0 || t.Rlist.Len() > 0 {
yyerror("func main must have no arguments and no return values")
}
}
f.Func.Nname = newfuncname(name)
} else {
// Receiver MethodName Signature
f.Func.Shortname = newfuncname(name)
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
}
f.Func.Nname.Name.Defn = f
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
declare(f.Func.Nname, PFUNC)
funchdr(f)
return f
}
func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *Node {
n := p.nod(typ, OTFUNC, nil, nil)
if recv != nil {
n.Left = p.param(recv, false, false)
}
n.List.Set(p.params(typ.ParamList, true))
n.Rlist.Set(p.params(typ.ResultList, false))
return n
}
func (p *noder) params(params []*syntax.Field, dddOk bool) []*Node {
var nodes []*Node
for i, param := range params {
p.lineno(param)
nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
}
return nodes
}
func (p *noder) param(param *syntax.Field, dddOk, final bool) *Node {
var name *Node
if param.Name != nil {
name = p.newname(param.Name)
}
typ := p.typeExpr(param.Type)
n := p.nod(param, ODCLFIELD, name, typ)
// rewrite ...T parameter
if typ.Op == ODDD {
if !dddOk {
yyerror("cannot use ... in receiver or result parameter list")
} else if !final {
yyerror("can only use ... with final parameter in list")
}
typ.Op = OTARRAY
typ.Right = typ.Left
typ.Left = nil
n.Isddd = true
if n.Left != nil {
n.Left.Isddd = true
}
}
return n
}
func (p *noder) exprList(expr syntax.Expr) []*Node {
if list, ok := expr.(*syntax.ListExpr); ok {
return p.exprs(list.ElemList)
}
return []*Node{p.expr(expr)}
}
func (p *noder) exprs(exprs []syntax.Expr) []*Node {
var nodes []*Node
for _, expr := range exprs {
nodes = append(nodes, p.expr(expr))
}
return nodes
}
func (p *noder) expr(expr syntax.Expr) *Node {
p.lineno(expr)
switch expr := expr.(type) {
case nil:
return nil
case *syntax.Name:
return p.mkname(expr)
case *syntax.BasicLit:
return p.setlineno(expr, nodlit(p.basicLit(expr)))
case *syntax.CompositeLit:
n := p.nod(expr, OCOMPLIT, nil, nil)
if expr.Type != nil {
n.Right = p.expr(expr.Type)
}
l := p.exprs(expr.ElemList)
for i, e := range l {
l[i] = p.wrapname(expr.ElemList[i], e)
}
n.List.Set(l)
lineno = p.baseline + int32(expr.EndLine) - 1
return n
case *syntax.KeyValueExpr:
return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
case *syntax.FuncLit:
closurehdr(p.typeExpr(expr.Type))
body := p.stmts(expr.Body)
lineno = p.baseline + int32(expr.EndLine) - 1
return p.setlineno(expr, closurebody(body))
case *syntax.ParenExpr:
return p.nod(expr, OPAREN, p.expr(expr.X), nil)
case *syntax.SelectorExpr:
// parser.new_dotname
obj := p.expr(expr.X)
if obj.Op == OPACK {
obj.Used = true
return oldname(restrictlookup(expr.Sel.Value, obj.Name.Pkg))
}
return p.setlineno(expr, nodSym(OXDOT, obj, p.name(expr.Sel)))
case *syntax.IndexExpr:
return p.nod(expr, OINDEX, p.expr(expr.X), p.expr(expr.Index))
case *syntax.SliceExpr:
op := OSLICE
if expr.Full {
op = OSLICE3
}
n := p.nod(expr, op, p.expr(expr.X), nil)
var index [3]*Node
for i, x := range expr.Index {
if x != nil {
index[i] = p.expr(x)
}
}
n.SetSliceBounds(index[0], index[1], index[2])
return n
case *syntax.AssertExpr:
if expr.Type == nil {
panic("unexpected AssertExpr")
}
// TODO(mdempsky): parser.pexpr uses p.expr(), but
// seems like the type field should be parsed with
// ntype? Shrug, doesn't matter here.
return p.nod(expr, ODOTTYPE, p.expr(expr.X), p.expr(expr.Type))
case *syntax.Operation:
x := p.expr(expr.X)
if expr.Y == nil {
if expr.Op == syntax.And {
x = unparen(x) // TODO(mdempsky): Needed?
if x.Op == OCOMPLIT {
// Special case for &T{...}: turn into (*T){...}.
// TODO(mdempsky): Switch back to p.nod after we
// get rid of gcCompat.
x.Right = nod(OIND, x.Right, nil)
x.Right.Implicit = true
return x
}
}
return p.nod(expr, p.unOp(expr.Op), x, nil)
}
return p.nod(expr, p.binOp(expr.Op), x, p.expr(expr.Y))
case *syntax.CallExpr:
n := p.nod(expr, OCALL, p.expr(expr.Fun), nil)
n.List.Set(p.exprs(expr.ArgList))
n.Isddd = expr.HasDots
return n
case *syntax.ArrayType:
var len *Node
if expr.Len != nil {
len = p.expr(expr.Len)
} else {
len = p.nod(expr, ODDD, nil, nil)
}
return p.nod(expr, OTARRAY, len, p.typeExpr(expr.Elem))
case *syntax.SliceType:
return p.nod(expr, OTARRAY, nil, p.typeExpr(expr.Elem))
case *syntax.DotsType:
return p.nod(expr, ODDD, p.typeExpr(expr.Elem), nil)
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 p.nod(expr, OTMAP, p.typeExpr(expr.Key), p.typeExpr(expr.Value))
case *syntax.ChanType:
n := p.nod(expr, OTCHAN, p.typeExpr(expr.Elem), nil)
n.Etype = EType(p.chanDir(expr.Dir))
return n
case *syntax.TypeSwitchGuard:
n := p.nod(expr, OTYPESW, nil, p.expr(expr.X))
if expr.Lhs != nil {
n.Left = p.declName(expr.Lhs)
if isblank(n.Left) {
yyerror("invalid variable name %v in type switch", n.Left)
}
}
return n
}
panic("unhandled Expr")
}
func (p *noder) typeExpr(typ syntax.Expr) *Node {
// TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
return p.expr(typ)
}
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
switch dir {
case 0:
return Cboth
case syntax.SendOnly:
return Csend
case syntax.RecvOnly:
return Crecv
}
panic("unhandled ChanDir")
}
func (p *noder) structType(expr *syntax.StructType) *Node {
var l []*Node
for i, field := range expr.FieldList {
p.lineno(field)
var n *Node
if field.Name == nil {
n = p.embedded(field.Type)
} else {
n = p.nod(field, ODCLFIELD, p.newname(field.Name), p.typeExpr(field.Type))
}
if i < len(expr.TagList) && expr.TagList[i] != nil {
n.SetVal(p.basicLit(expr.TagList[i]))
}
l = append(l, n)
}
p.lineno(expr)
n := p.nod(expr, OTSTRUCT, nil, nil)
n.List.Set(l)
return n
}
func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
var l []*Node
for _, method := range expr.MethodList {
p.lineno(method)
var n *Node
if method.Name == nil {
n = p.nod(method, ODCLFIELD, nil, oldname(p.packname(method.Type)))
} else {
mname := p.newname(method.Name)
sig := p.typeExpr(method.Type)
sig.Left = fakethis()
n = p.nod(method, ODCLFIELD, mname, sig)
ifacedcl(n)
}
l = append(l, n)
}
n := p.nod(expr, OTINTER, nil, nil)
n.List.Set(l)
return n
}
func (p *noder) packname(expr syntax.Expr) *Sym {
switch expr := expr.(type) {
case *syntax.Name:
name := p.name(expr)
if n := oldname(name); n.Name != nil && n.Name.Pack != nil {
n.Name.Pack.Used = true
}
return name
case *syntax.SelectorExpr:
name := p.name(expr.X.(*syntax.Name))
var pkg *Pkg
if name.Def == nil || name.Def.Op != OPACK {
yyerror("%v is not a package", name)
pkg = localpkg
} else {
name.Def.Used = true
pkg = name.Def.Name.Pkg
}
return restrictlookup(expr.Sel.Value, pkg)
}
panic(fmt.Sprintf("unexpected packname: %#v", expr))
}
func (p *noder) embedded(typ syntax.Expr) *Node {
op, isStar := typ.(*syntax.Operation)
if isStar {
if op.Op != syntax.Mul || op.Y != nil {
panic("unexpected Operation")
}
typ = op.X
}
n := embedded(p.packname(typ), localpkg)
if isStar {
n.Right = p.nod(op, OIND, n.Right, nil)
}
return n
}
func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
var nodes []*Node
for _, stmt := range stmts {
s := p.stmt(stmt)
if s == nil {
} else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
nodes = append(nodes, s.List.Slice()...)
} else {
nodes = append(nodes, s)
}
}
return nodes
}
func (p *noder) stmt(stmt syntax.Stmt) *Node {
p.lineno(stmt)
switch stmt := stmt.(type) {
case *syntax.EmptyStmt:
return nil
case *syntax.LabeledStmt:
return p.labeledStmt(stmt)
case *syntax.BlockStmt:
return p.body(stmt.Body)
case *syntax.ExprStmt:
return p.wrapname(stmt, p.expr(stmt.X))
case *syntax.SendStmt:
return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
case *syntax.DeclStmt:
return liststmt(p.decls(stmt.DeclList))
case *syntax.AssignStmt:
if stmt.Op != 0 && stmt.Op != syntax.Def {
n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
n.Implicit = stmt.Rhs == syntax.ImplicitOne
n.Etype = EType(p.binOp(stmt.Op))
return n
}
lhs := p.exprList(stmt.Lhs)
rhs := p.exprList(stmt.Rhs)
n := p.nod(stmt, OAS, nil, nil) // assume common case
if stmt.Op == syntax.Def {
n.Colas = true
colasdefn(lhs, n) // modifies lhs, call before using lhs[0] in common case
}
if len(lhs) == 1 && len(rhs) == 1 {
// common case
n.Left = lhs[0]
n.Right = rhs[0]
} else {
n.Op = OAS2
n.List.Set(lhs)
n.Rlist.Set(rhs)
}
return n
case *syntax.BranchStmt:
var op Op
switch stmt.Tok {
case syntax.Break:
op = OBREAK
case syntax.Continue:
op = OCONTINUE
case syntax.Fallthrough:
op = OXFALL
case syntax.Goto:
op = OGOTO
default:
panic("unhandled BranchStmt")
}
n := p.nod(stmt, op, nil, nil)
if stmt.Label != nil {
n.Left = p.newname(stmt.Label)
}
if op == OGOTO {
n.Sym = dclstack // context, for goto restriction
}
if op == OXFALL {
n.Xoffset = int64(block)
}
return n
case *syntax.CallStmt:
var op Op
switch stmt.Tok {
case syntax.Defer:
op = ODEFER
case syntax.Go:
op = OPROC
default:
panic("unhandled CallStmt")
}
return p.nod(stmt, op, p.expr(stmt.Call), nil)
case *syntax.ReturnStmt:
var results []*Node
if stmt.Results != nil {
results = p.exprList(stmt.Results)
}
n := p.nod(stmt, ORETURN, nil, nil)
n.List.Set(results)
if n.List.Len() == 0 && Curfn != nil {
for _, ln := range Curfn.Func.Dcl {
if ln.Class == PPARAM {
continue
}
if ln.Class != PPARAMOUT {
break
}
if ln.Sym.Def != ln {
yyerror("%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) body(body []syntax.Stmt) *Node {
l := p.bodyList(body)
if len(l) == 0 {
// TODO(mdempsky): Line number?
return nod(OEMPTY, nil, nil)
}
return liststmt(l)
}
func (p *noder) bodyList(body []syntax.Stmt) []*Node {
markdcl()
nodes := p.stmts(body)
popdcl()
return nodes
}
func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
markdcl()
n := p.nod(stmt, OIF, nil, nil)
if stmt.Init != nil {
n.Ninit.Set1(p.stmt(stmt.Init))
}
if stmt.Cond != nil {
n.Left = p.expr(stmt.Cond)
}
n.Nbody.Set(p.bodyList(stmt.Then))
if stmt.Else != nil {
e := p.stmt(stmt.Else)
if e.Op == OBLOCK && e.Ninit.Len() == 0 {
n.Rlist.Set(e.List.Slice())
} else {
n.Rlist.Set1(e)
}
}
popdcl()
return n
}
func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
markdcl()
var n *Node
if r, ok := stmt.Init.(*syntax.RangeClause); ok {
if stmt.Cond != nil || stmt.Post != nil {
panic("unexpected RangeClause")
}
n = p.nod(r, ORANGE, nil, p.expr(r.X))
if r.Lhs != nil {
lhs := p.exprList(r.Lhs)
n.List.Set(lhs)
if r.Def {
n.Colas = true
colasdefn(lhs, n)
}
}
} else {
n = p.nod(stmt, OFOR, nil, nil)
if stmt.Init != nil {
n.Ninit.Set1(p.stmt(stmt.Init))
}
if stmt.Cond != nil {
n.Left = p.expr(stmt.Cond)
}
if stmt.Post != nil {
n.Right = p.stmt(stmt.Post)
}
}
n.Nbody.Set(p.bodyList(stmt.Body))
popdcl()
return n
}
func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
markdcl()
n := p.nod(stmt, OSWITCH, nil, nil)
if stmt.Init != nil {
n.Ninit.Set1(p.stmt(stmt.Init))
}
if stmt.Tag != nil {
n.Left = p.expr(stmt.Tag)
}
tswitch := n.Left
if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
tswitch = nil
}
n.List.Set(p.caseClauses(stmt.Body, tswitch))
popdcl()
return n
}
func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node {
var nodes []*Node
for _, clause := range clauses {
p.lineno(clause)
markdcl()
n := p.nod(clause, OXCASE, nil, nil)
if clause.Cases != nil {
n.List.Set(p.exprList(clause.Cases))
}
if tswitch != nil {
nn := newname(tswitch.Left.Sym)
declare(nn, dclcontext)
n.Rlist.Set1(nn)
// keep track of the instances for reporting unused
nn.Name.Defn = tswitch
}
n.Xoffset = int64(block)
n.Nbody.Set(p.stmts(clause.Body))
popdcl()
nodes = append(nodes, n)
}
return nodes
}
func (p *noder) selectStmt(stmt *syntax.SelectStmt) *Node {
n := p.nod(stmt, OSELECT, nil, nil)
n.List.Set(p.commClauses(stmt.Body))
return n
}
func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node {
var nodes []*Node
for _, clause := range clauses {
p.lineno(clause)
markdcl()
n := p.nod(clause, OXCASE, nil, nil)
if clause.Comm != nil {
n.List.Set1(p.stmt(clause.Comm))
}
n.Xoffset = int64(block)
n.Nbody.Set(p.stmts(clause.Body))
popdcl()
nodes = append(nodes, n)
}
return nodes
}
func (p *noder) labeledStmt(label *syntax.LabeledStmt) *Node {
lhs := p.nod(label, OLABEL, p.newname(label.Label), nil)
lhs.Sym = dclstack
var ls *Node
if label.Stmt != nil { // TODO(mdempsky): Should always be present.
ls = p.stmt(label.Stmt)
}
lhs.Name.Defn = ls
l := []*Node{lhs}
if ls != nil {
if ls.Op == OBLOCK && ls.Ninit.Len() == 0 {
l = append(l, ls.List.Slice()...)
} else {
l = append(l, ls)
}
}
return liststmt(l)
}
var unOps = [...]Op{
syntax.Recv: ORECV,
syntax.Mul: OIND,
syntax.And: OADDR,
syntax.Not: ONOT,
syntax.Xor: OCOM,
syntax.Add: OPLUS,
syntax.Sub: OMINUS,
}
func (p *noder) unOp(op syntax.Operator) Op {
if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
panic("invalid Operator")
}
return unOps[op]
}
var binOps = [...]Op{
syntax.OrOr: OOROR,
syntax.AndAnd: OANDAND,
syntax.Eql: OEQ,
syntax.Neq: ONE,
syntax.Lss: OLT,
syntax.Leq: OLE,
syntax.Gtr: OGT,
syntax.Geq: OGE,
syntax.Add: OADD,
syntax.Sub: OSUB,
syntax.Or: OOR,
syntax.Xor: OXOR,
syntax.Mul: OMUL,
syntax.Div: ODIV,
syntax.Rem: OMOD,
syntax.And: OAND,
syntax.AndNot: OANDNOT,
syntax.Shl: OLSH,
syntax.Shr: ORSH,
}
func (p *noder) binOp(op syntax.Operator) Op {
if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 {
panic("invalid Operator")
}
return binOps[op]
}
func (p *noder) basicLit(lit *syntax.BasicLit) Val {
// TODO: Don't try to convert if we had syntax errors (conversions may fail).
// Use dummy values so we can continue to compile. Eventually, use a
// form of "unknown" literals that are ignored during type-checking so
// we can continue type-checking w/o spurious follow-up errors.
switch s := lit.Value; lit.Kind {
case syntax.IntLit:
x := new(Mpint)
x.SetString(s)
return Val{U: x}
case syntax.FloatLit:
x := newMpflt()
x.SetString(s)
return Val{U: x}
case syntax.ImagLit:
x := new(Mpcplx)
x.Imag.SetString(strings.TrimSuffix(s, "i"))
return Val{U: x}
case syntax.RuneLit:
var r rune
if u, err := strconv.Unquote(s); err == nil && len(u) > 0 {
// Package syntax already reported any errors.
// Check for them again though because 0 is a
// better fallback value for invalid rune
// literals than 0xFFFD.
if len(u) == 1 {
r = rune(u[0])
} else {
r, _ = utf8.DecodeRuneInString(u)
}
}
x := new(Mpint)
x.SetInt64(int64(r))
x.Rune = true
return Val{U: x}
case syntax.StringLit:
if len(s) > 0 && s[0] == '`' {
// strip carriage returns from raw string
s = strings.Replace(s, "\r", "", -1)
}
// Ignore errors because package syntax already reported them.
u, _ := strconv.Unquote(s)
return Val{U: u}
default:
panic("unhandled BasicLit kind")
}
}
func (p *noder) name(name *syntax.Name) *Sym {
return lookup(name.Value)
}
func (p *noder) mkname(name *syntax.Name) *Node {
// TODO(mdempsky): Set line number?
return mkname(p.name(name))
}
func (p *noder) newname(name *syntax.Name) *Node {
// TODO(mdempsky): Set line number?
return newname(p.name(name))
}
func (p *noder) wrapname(n syntax.Node, x *Node) *Node {
// These nodes do not carry line numbers.
// Introduce a wrapper node to give them the correct line.
switch x.Op {
case OTYPE, OLITERAL:
if x.Sym == nil {
break
}
fallthrough
case ONAME, ONONAME, OPACK:
x = p.nod(n, OPAREN, x, nil)
x.Implicit = true
}
return x
}
func (p *noder) nod(orig syntax.Node, op Op, left, right *Node) *Node {
return p.setlineno(orig, nod(op, left, right))
}
func (p *noder) setlineno(src syntax.Node, dst *Node) *Node {
l := int32(src.Line())
if l == 0 {
// TODO(mdempsky): Shouldn't happen. Fix package syntax.
return dst
}
dst.Lineno = p.baseline + l - 1
return dst
}
func (p *noder) lineno(n syntax.Node) {
if n == nil {
return
}
l := int32(n.Line())
if l == 0 {
// TODO(mdempsky): Shouldn't happen. Fix package syntax.
return
}
lineno = p.baseline + l - 1
}
func (p *noder) error(_, line int, msg string) {
yyerrorl(p.baseline+int32(line)-1, "%s", msg)
}
func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
switch {
case strings.HasPrefix(text, "line "):
i := strings.IndexByte(text, ':')
if i < 0 {
break
}
n, err := strconv.Atoi(text[i+1:])
if err != nil {
// todo: make this an error instead? it is almost certainly a bug.
break
}
if n > 1e8 {
p.error(pos, line, "line number out of range")
errorexit()
}
if n <= 0 {
break
}
lexlineno = p.baseline + int32(line)
linehistupdate(text[5:i], n)
case strings.HasPrefix(text, "go:linkname "):
// Record line number so we can emit an error later if
// the file doesn't import package unsafe.
p.linknames = append(p.linknames, line)
f := strings.Fields(text)
if len(f) != 3 {
p.error(pos, line, "usage: //go:linkname localname linkname")
break
}
lookup(f[1]).Linkname = f[2]
case strings.HasPrefix(text, "go:cgo_"):
pragcgobuf += pragcgo(text)
fallthrough // because of //go:cgo_unsafe_args
default:
verb := text
if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i]
}
return syntax.Pragma(pragmaValue(verb))
}
return 0
}
func mkname(sym *Sym) *Node {
n := oldname(sym)
if n.Name != nil && n.Name.Pack != nil {
n.Name.Pack.Used = true
}
return n
}
func unparen(x *Node) *Node {
for x.Op == OPAREN {
x = x.Left
}
return x
}