aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/noder/decl.go222
-rw-r--r--src/cmd/compile/internal/noder/expr.go204
-rw-r--r--src/cmd/compile/internal/noder/func.go74
-rw-r--r--src/cmd/compile/internal/noder/helpers.go130
-rw-r--r--src/cmd/compile/internal/noder/irgen.go176
-rw-r--r--src/cmd/compile/internal/noder/noder.go178
-rw-r--r--src/cmd/compile/internal/noder/object.go169
-rw-r--r--src/cmd/compile/internal/noder/posmap.go6
-rw-r--r--src/cmd/compile/internal/noder/scopes.go64
-rw-r--r--src/cmd/compile/internal/noder/stmt.go280
-rw-r--r--src/cmd/compile/internal/noder/types.go208
-rw-r--r--src/cmd/compile/internal/noder/validate.go113
-rw-r--r--src/cmd/compile/internal/typecheck/dcl.go5
-rw-r--r--src/cmd/compile/internal/typecheck/func.go4
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go8
-rw-r--r--src/cmd/compile/internal/types/scope.go2
16 files changed, 1685 insertions, 158 deletions
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
new file mode 100644
index 0000000000..ce5bad88f3
--- /dev/null
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -0,0 +1,222 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// TODO(mdempsky): Skip blank declarations? Probably only safe
+// for declarations without pragmas.
+
+func (g *irgen) decls(decls []syntax.Decl) []ir.Node {
+ var res ir.Nodes
+ for _, decl := range decls {
+ switch decl := decl.(type) {
+ case *syntax.ConstDecl:
+ g.constDecl(&res, decl)
+ case *syntax.FuncDecl:
+ g.funcDecl(&res, decl)
+ case *syntax.TypeDecl:
+ if ir.CurFunc == nil {
+ continue // already handled in irgen.generate
+ }
+ g.typeDecl(&res, decl)
+ case *syntax.VarDecl:
+ g.varDecl(&res, decl)
+ default:
+ g.unhandled("declaration", decl)
+ }
+ }
+ return res
+}
+
+func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
+ // TODO(mdempsky): Merge with gcimports so we don't have to import
+ // packages twice.
+
+ g.pragmaFlags(decl.Pragma, 0)
+
+ ipkg := importfile(decl)
+ if ipkg == ir.Pkgs.Unsafe {
+ p.importedUnsafe = true
+ }
+}
+
+func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
+ g.pragmaFlags(decl.Pragma, 0)
+
+ for _, name := range decl.NameList {
+ name, obj := g.def(name)
+ name.SetVal(obj.(*types2.Const).Val())
+ out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name))
+ }
+}
+
+func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
+ fn := ir.NewFunc(g.pos(decl))
+ fn.Nname, _ = g.def(decl.Name)
+ fn.Nname.Func = fn
+ fn.Nname.Defn = fn
+
+ fn.Pragma = g.pragmaFlags(decl.Pragma, funcPragmas)
+ if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 {
+ base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined")
+ }
+
+ if decl.Name.Value == "init" && decl.Recv == nil {
+ g.target.Inits = append(g.target.Inits, fn)
+ }
+
+ g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
+
+ out.Append(fn)
+}
+
+func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
+ if decl.Alias {
+ if !types.AllowsGoVersion(types.LocalPkg, 1, 9) {
+ base.ErrorfAt(g.pos(decl), "type aliases only supported as of -lang=go1.9")
+ }
+
+ name, _ := g.def(decl.Name)
+ g.pragmaFlags(decl.Pragma, 0)
+
+ // TODO(mdempsky): This matches how typecheckdef marks aliases for
+ // export, but this won't generalize to exporting function-scoped
+ // type aliases. We should maybe just use n.Alias() instead.
+ if ir.CurFunc == nil {
+ name.Sym().Def = ir.TypeNode(name.Type())
+ }
+
+ out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
+ return
+ }
+
+ name, obj := g.def(decl.Name)
+ ntyp, otyp := name.Type(), obj.Type()
+ if ir.CurFunc != nil {
+ typecheck.TypeGen++
+ ntyp.Vargen = typecheck.TypeGen
+ }
+
+ pragmas := g.pragmaFlags(decl.Pragma, typePragmas)
+ name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed?
+
+ if pragmas&ir.NotInHeap != 0 {
+ ntyp.SetNotInHeap(true)
+ }
+
+ // We need to use g.typeExpr(decl.Type) here to ensure that for
+ // chained, defined-type declarations like
+ //
+ // type T U
+ //
+ // //go:notinheap
+ // type U struct { … }
+ //
+ // that we mark both T and U as NotInHeap. If we instead used just
+ // g.typ(otyp.Underlying()), then we'd instead set T's underlying
+ // type directly to the struct type (which is not marked NotInHeap)
+ // and fail to mark T as NotInHeap.
+ //
+ // Also, we rely here on Type.SetUnderlying allowing passing a
+ // defined type and handling forward references like from T to U
+ // above. Contrast with go/types's Named.SetUnderlying, which
+ // disallows this.
+ //
+ // [mdempsky: Subtleties like these are why I always vehemently
+ // object to new type pragmas.]
+ ntyp.SetUnderlying(g.typeExpr(decl.Type))
+
+ if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
+ methods := make([]*types.Field, otyp.NumMethods())
+ for i := range methods {
+ m := otyp.Method(i)
+ meth := g.obj(m)
+ methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
+ methods[i].Nname = meth
+ }
+ ntyp.Methods().Set(methods)
+ }
+
+ out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
+}
+
+func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
+ pos := g.pos(decl)
+ names := make([]*ir.Name, len(decl.NameList))
+ for i, name := range decl.NameList {
+ names[i], _ = g.def(name)
+ }
+ values := g.exprList(decl.Values)
+
+ if decl.Pragma != nil {
+ pragma := decl.Pragma.(*pragmas)
+ if err := varEmbed(g.makeXPos, names[0], decl, pragma); err != nil {
+ base.ErrorfAt(g.pos(decl), "%s", err.Error())
+ }
+ g.reportUnused(pragma)
+ }
+
+ var as2 *ir.AssignListStmt
+ if len(values) != 0 && len(names) != len(values) {
+ as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values)
+ }
+
+ for i, name := range names {
+ if ir.CurFunc != nil {
+ out.Append(ir.NewDecl(pos, ir.ODCL, name))
+ }
+ if as2 != nil {
+ as2.Lhs[i] = name
+ name.Defn = as2
+ } else {
+ as := ir.NewAssignStmt(pos, name, nil)
+ if len(values) != 0 {
+ as.Y = values[i]
+ name.Defn = as
+ } else if ir.CurFunc == nil {
+ name.Defn = as
+ }
+ out.Append(typecheck.Stmt(as))
+ }
+ }
+ if as2 != nil {
+ out.Append(typecheck.Stmt(as2))
+ }
+}
+
+// pragmaFlags returns any specified pragma flags included in allowed,
+// and reports errors about any other, unexpected pragmas.
+func (g *irgen) pragmaFlags(pragma syntax.Pragma, allowed ir.PragmaFlag) ir.PragmaFlag {
+ if pragma == nil {
+ return 0
+ }
+ p := pragma.(*pragmas)
+ present := p.Flag & allowed
+ p.Flag &^= allowed
+ g.reportUnused(p)
+ return present
+}
+
+// reportUnused reports errors about any unused pragmas.
+func (g *irgen) reportUnused(pragma *pragmas) {
+ for _, pos := range pragma.Pos {
+ if pos.Flag&pragma.Flag != 0 {
+ base.ErrorfAt(g.makeXPos(pos.Pos), "misplaced compiler directive")
+ }
+ }
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ base.ErrorfAt(g.makeXPos(e.Pos), "misplaced go:embed directive")
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
new file mode 100644
index 0000000000..fba6ad2e4b
--- /dev/null
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -0,0 +1,204 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+func (g *irgen) expr(expr syntax.Expr) ir.Node {
+ // TODO(mdempsky): Change callers to not call on nil?
+ if expr == nil {
+ return nil
+ }
+
+ if expr == syntax.ImplicitOne {
+ base.Fatalf("expr of ImplicitOne")
+ }
+
+ if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" {
+ return ir.BlankNode
+ }
+
+ // TODO(mdempsky): Is there a better way to recognize and handle qualified identifiers?
+ if expr, ok := expr.(*syntax.SelectorExpr); ok {
+ if name, ok := expr.X.(*syntax.Name); ok {
+ if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
+ return g.use(expr.Sel)
+ }
+ }
+ }
+
+ tv, ok := g.info.Types[expr]
+ if !ok {
+ base.FatalfAt(g.pos(expr), "missing type for %v (%T)", expr, expr)
+ }
+ switch {
+ case tv.IsBuiltin():
+ // TODO(mdempsky): Handle in CallExpr?
+ return g.use(expr.(*syntax.Name))
+ case tv.IsType():
+ return ir.TypeNode(g.typ(tv.Type))
+ case tv.IsValue(), tv.IsVoid():
+ // ok
+ default:
+ base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
+ }
+
+ // Constant expression.
+ if tv.Value != nil {
+ return Const(g.pos(expr), g.typ(tv.Type), tv.Value)
+ }
+
+ // TODO(mdempsky): Remove dependency on typecheck.Expr.
+ n := typecheck.Expr(g.expr0(tv.Type, expr))
+ if !g.match(n.Type(), tv.Type, tv.HasOk()) {
+ base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, tv.Type)
+ }
+ return n
+}
+
+func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
+ pos := g.pos(expr)
+
+ switch expr := expr.(type) {
+ case *syntax.Name:
+ if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
+ return Nil(pos, g.typ(typ))
+ }
+ return g.use(expr)
+
+ case *syntax.CompositeLit:
+ return g.compLit(typ, expr)
+ case *syntax.FuncLit:
+ return g.funcLit(typ, expr)
+
+ case *syntax.AssertExpr:
+ return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type))
+ case *syntax.CallExpr:
+ return Call(pos, g.expr(expr.Fun), g.exprs(expr.ArgList), expr.HasDots)
+ case *syntax.IndexExpr:
+ return Index(pos, g.expr(expr.X), g.expr(expr.Index))
+ case *syntax.ParenExpr:
+ return g.expr(expr.X) // skip parens; unneeded after parse+typecheck
+ case *syntax.SelectorExpr:
+ // TODO(mdempsky/danscales): Use g.info.Selections[expr]
+ // to resolve field/method selection. See CL 280633.
+ return ir.NewSelectorExpr(pos, ir.OXDOT, g.expr(expr.X), g.name(expr.Sel))
+ case *syntax.SliceExpr:
+ return Slice(pos, g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
+
+ case *syntax.Operation:
+ if expr.Y == nil {
+ return Unary(pos, g.op(expr.Op, unOps[:]), g.expr(expr.X))
+ }
+ switch op := g.op(expr.Op, binOps[:]); op {
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ return Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
+ default:
+ return Binary(pos, op, g.expr(expr.X), g.expr(expr.Y))
+ }
+
+ default:
+ g.unhandled("expression", expr)
+ panic("unreachable")
+ }
+}
+
+func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
+ switch expr := expr.(type) {
+ case nil:
+ return nil
+ case *syntax.ListExpr:
+ return g.exprs(expr.ElemList)
+ default:
+ return []ir.Node{g.expr(expr)}
+ }
+}
+
+func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
+ nodes := make([]ir.Node, len(exprs))
+ for i, expr := range exprs {
+ nodes[i] = g.expr(expr)
+ }
+ return nodes
+}
+
+func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
+ if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
+ if _, isNamed := typ.(*types2.Named); isNamed {
+ // TODO(mdempsky): Questionable, but this is
+ // currently allowed by cmd/compile, go/types,
+ // and gccgo:
+ //
+ // type T *struct{}
+ // var _ = []T{{}}
+ base.FatalfAt(g.pos(lit), "defined-pointer composite literal")
+ }
+ return ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
+ }
+
+ _, isStruct := typ.Underlying().(*types2.Struct)
+
+ exprs := make([]ir.Node, len(lit.ElemList))
+ for i, elem := range lit.ElemList {
+ switch elem := elem.(type) {
+ case *syntax.KeyValueExpr:
+ if isStruct {
+ exprs[i] = ir.NewStructKeyExpr(g.pos(elem), g.name(elem.Key.(*syntax.Name)), g.expr(elem.Value))
+ } else {
+ exprs[i] = ir.NewKeyExpr(g.pos(elem), g.expr(elem.Key), g.expr(elem.Value))
+ }
+ default:
+ exprs[i] = g.expr(elem)
+ }
+ }
+
+ return ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, ir.TypeNode(g.typ(typ)), exprs)
+}
+
+func (g *irgen) funcLit(typ types2.Type, expr *syntax.FuncLit) ir.Node {
+ fn := ir.NewFunc(g.pos(expr))
+ fn.SetIsHiddenClosure(ir.CurFunc != nil)
+
+ fn.Nname = ir.NewNameAt(g.pos(expr), typecheck.ClosureName(ir.CurFunc))
+ ir.MarkFunc(fn.Nname)
+ fn.Nname.SetType(g.typ(typ))
+ fn.Nname.Func = fn
+ fn.Nname.Defn = fn
+
+ fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn)
+ fn.OClosure.SetType(fn.Nname.Type())
+ fn.OClosure.SetTypecheck(1)
+
+ g.funcBody(fn, nil, expr.Type, expr.Body)
+
+ ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn)
+
+ // TODO(mdempsky): ir.CaptureName should probably handle
+ // copying these fields from the canonical variable.
+ for _, cv := range fn.ClosureVars {
+ cv.SetType(cv.Canonical().Type())
+ cv.SetTypecheck(1)
+ cv.SetWalkdef(1)
+ }
+
+ g.target.Decls = append(g.target.Decls, fn)
+
+ return fn.OClosure
+}
+
+func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
+ n := g.expr(typ)
+ if n.Op() != ir.OTYPE {
+ base.FatalfAt(g.pos(typ), "expected type: %L", n)
+ }
+ return n.Type()
+}
diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go
new file mode 100644
index 0000000000..702138157c
--- /dev/null
+++ b/src/cmd/compile/internal/noder/func.go
@@ -0,0 +1,74 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, block *syntax.BlockStmt) {
+ typecheck.Func(fn)
+
+ // TODO(mdempsky): Remove uses of ir.CurFunc and
+ // typecheck.DeclContext after we stop relying on typecheck
+ // for desugaring.
+ outerfn, outerctxt := ir.CurFunc, typecheck.DeclContext
+ ir.CurFunc = fn
+
+ typ := fn.Type()
+ if param := typ.Recv(); param != nil {
+ g.defParam(param, recv, ir.PPARAM)
+ }
+ for i, param := range typ.Params().FieldSlice() {
+ g.defParam(param, sig.ParamList[i], ir.PPARAM)
+ }
+ for i, result := range typ.Results().FieldSlice() {
+ g.defParam(result, sig.ResultList[i], ir.PPARAMOUT)
+ }
+
+ // We may have type-checked a call to this function already and
+ // calculated its size, including parameter offsets. Now that we've
+ // created the parameter Names, force a recalculation to ensure
+ // their offsets are correct.
+ typ.Align = 0
+ types.CalcSize(typ)
+
+ if block != nil {
+ typecheck.DeclContext = ir.PAUTO
+
+ fn.Body = g.stmts(block.List)
+ if fn.Body == nil {
+ fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
+ }
+ fn.Endlineno = g.makeXPos(block.Rbrace)
+
+ if base.Flag.Dwarf {
+ g.recordScopes(fn, sig)
+ }
+ }
+
+ ir.CurFunc, typecheck.DeclContext = outerfn, outerctxt
+}
+
+func (g *irgen) defParam(param *types.Field, decl *syntax.Field, class ir.Class) {
+ typecheck.DeclContext = class
+
+ var name *ir.Name
+ if decl.Name != nil {
+ name, _ = g.def(decl.Name)
+ } else if class == ir.PPARAMOUT {
+ name = g.obj(g.info.Implicits[decl])
+ }
+
+ if name != nil {
+ param.Nname = name
+ param.Sym = name.Sym() // in case it was renamed
+ }
+}
diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go
new file mode 100644
index 0000000000..2139f16a6c
--- /dev/null
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -0,0 +1,130 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "go/constant"
+)
+
+// Helpers for constructing typed IR nodes.
+//
+// TODO(mdempsky): Move into their own package so they can be easily
+// reused by iimport and frontend optimizations.
+//
+// TODO(mdempsky): Update to consistently return already typechecked
+// results, rather than leaving the caller responsible for using
+// typecheck.Expr or typecheck.Stmt.
+
+// Values
+
+func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
+ n := ir.NewBasicLit(pos, val)
+ n.SetType(typ)
+ return n
+}
+
+func Nil(pos src.XPos, typ *types.Type) ir.Node {
+ n := ir.NewNilExpr(pos)
+ n.SetType(typ)
+ return n
+}
+
+// Expressions
+
+func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
+ return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, ir.TypeNode(typ)))
+}
+
+func Binary(pos src.XPos, op ir.Op, x, y ir.Node) ir.Node {
+ switch op {
+ case ir.OANDAND, ir.OOROR:
+ return ir.NewLogicalExpr(pos, op, x, y)
+ default:
+ return ir.NewBinaryExpr(pos, op, x, y)
+ }
+}
+
+func Call(pos src.XPos, fun ir.Node, args []ir.Node, dots bool) ir.Node {
+ // TODO(mdempsky): This should not be so difficult.
+
+ n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
+ n.IsDDD = dots
+
+ // Actually a type conversion.
+ if fun.Op() == ir.OTYPE {
+ return typecheck.Expr(n)
+ }
+
+ if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
+ switch fun.BuiltinOp {
+ case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN:
+ return typecheck.Stmt(n)
+ default:
+ return typecheck.Expr(n)
+ }
+ }
+
+ // We probably already typechecked fun, and typecheck probably
+ // got it wrong because it didn't know the expression was
+ // going to be called immediately. Correct its mistakes.
+ switch fun := fun.(type) {
+ case *ir.ClosureExpr:
+ fun.Func.SetClosureCalled(true)
+ case *ir.SelectorExpr:
+ if fun.Op() == ir.OCALLPART {
+ op := ir.ODOTMETH
+ if fun.X.Type().IsInterface() {
+ op = ir.ODOTINTER
+ }
+ fun.SetOp(op)
+ fun.SetType(fun.Selection.Type)
+ }
+ }
+
+ typecheck.Call(n)
+ return n
+}
+
+func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
+ n := typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+ n.SetType(typ)
+ return n
+}
+
+func Index(pos src.XPos, x, index ir.Node) ir.Node {
+ return ir.NewIndexExpr(pos, x, index)
+}
+
+func Slice(pos src.XPos, x, low, high, max ir.Node) ir.Node {
+ op := ir.OSLICE
+ if max != nil {
+ op = ir.OSLICE3
+ }
+ return ir.NewSliceExpr(pos, op, x, low, high, max)
+}
+
+func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
+ switch op {
+ case ir.OADDR:
+ return typecheck.NodAddrAt(pos, x)
+ case ir.ODEREF:
+ return ir.NewStarExpr(pos, x)
+ default:
+ return ir.NewUnaryExpr(pos, op, x)
+ }
+}
+
+// Statements
+
+var one = constant.MakeInt64(1)
+
+func IncDec(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
+ x = typecheck.AssignExpr(x)
+ return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
+}
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
new file mode 100644
index 0000000000..694a6abb8e
--- /dev/null
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -0,0 +1,176 @@
+// Copyright 2021 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 (
+ "os"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+// check2 type checks a Go package using types2, and then generates IR
+// using the results.
+func check2(noders []*noder) {
+ if base.SyntaxErrors() != 0 {
+ base.ErrorExit()
+ }
+
+ // setup and syntax error reporting
+ var m posMap
+ files := make([]*syntax.File, len(noders))
+ for i, p := range noders {
+ m.join(&p.posMap)
+ files[i] = p.file
+ }
+
+ // typechecking
+ conf := types2.Config{
+ InferFromConstraints: true,
+ IgnoreBranches: true, // parser already checked via syntax.CheckBranches mode
+ CompilerErrorMessages: true, // use error strings matching existing compiler errors
+ Error: func(err error) {
+ terr := err.(types2.Error)
+ if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
+ // types2 reports error clarifications via separate
+ // error messages which are indented with a tab.
+ // Ignore them to satisfy tools and tests that expect
+ // only one error in such cases.
+ // TODO(gri) Need to adjust error reporting in types2.
+ return
+ }
+ base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
+ },
+ Importer: &gcimports{
+ packages: make(map[string]*types2.Package),
+ },
+ Sizes: &gcSizes{},
+ }
+ info := types2.Info{
+ Types: make(map[syntax.Expr]types2.TypeAndValue),
+ Defs: make(map[*syntax.Name]types2.Object),
+ Uses: make(map[*syntax.Name]types2.Object),
+ Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
+ Implicits: make(map[syntax.Node]types2.Object),
+ Scopes: make(map[syntax.Node]*types2.Scope),
+ // expand as needed
+ }
+ pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
+ files = nil
+ if err != nil {
+ base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
+ }
+ base.ExitIfErrors()
+ if base.Flag.G < 2 {
+ os.Exit(0)
+ }
+
+ g := irgen{
+ target: typecheck.Target,
+ self: pkg,
+ info: &info,
+ posMap: m,
+ objs: make(map[types2.Object]*ir.Name),
+ }
+ g.generate(noders)
+
+ if base.Flag.G < 3 {
+ os.Exit(0)
+ }
+}
+
+type irgen struct {
+ target *ir.Package
+ self *types2.Package
+ info *types2.Info
+
+ posMap
+ objs map[types2.Object]*ir.Name
+ marker dwarfgen.ScopeMarker
+}
+
+func (g *irgen) generate(noders []*noder) {
+ types.LocalPkg.Name = g.self.Name()
+ typecheck.TypecheckAllowed = true
+
+ // At this point, types2 has already handled name resolution and
+ // type checking. We just need to map from its object and type
+ // representations to those currently used by the rest of the
+ // compiler. This happens mostly in 3 passes.
+
+ // 1. Process all import declarations. We use the compiler's own
+ // importer for this, rather than types2's gcimporter-derived one,
+ // to handle extensions and inline function bodies correctly.
+ //
+ // Also, we need to do this in a separate pass, because mappings are
+ // instantiated on demand. If we interleaved processing import
+ // declarations with other declarations, it's likely we'd end up
+ // wanting to map an object/type from another source file, but not
+ // yet have the import data it relies on.
+ declLists := make([][]syntax.Decl, len(noders))
+Outer:
+ for i, p := range noders {
+ g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma)
+ for j, decl := range p.file.DeclList {
+ switch decl := decl.(type) {
+ case *syntax.ImportDecl:
+ g.importDecl(p, decl)
+ default:
+ declLists[i] = p.file.DeclList[j:]
+ continue Outer // no more ImportDecls
+ }
+ }
+ }
+ types.LocalPkg.Height = myheight
+
+ // 2. Process all package-block type declarations. As with imports,
+ // we need to make sure all types are properly instantiated before
+ // trying to map any expressions that utilize them. In particular,
+ // we need to make sure type pragmas are already known (see comment
+ // in irgen.typeDecl).
+ //
+ // We could perhaps instead defer processing of package-block
+ // variable initializers and function bodies, like noder does, but
+ // special-casing just package-block type declarations minimizes the
+ // differences between processing package-block and function-scoped
+ // declarations.
+ for _, declList := range declLists {
+ for _, decl := range declList {
+ switch decl := decl.(type) {
+ case *syntax.TypeDecl:
+ g.typeDecl((*ir.Nodes)(&g.target.Decls), decl)
+ }
+ }
+ }
+
+ // 3. Process all remaining declarations.
+ for _, declList := range declLists {
+ g.target.Decls = append(g.target.Decls, g.decls(declList)...)
+ }
+ typecheck.DeclareUniverse()
+
+ for _, p := range noders {
+ // Process linkname and cgo pragmas.
+ p.processPragmas()
+
+ // Double check for any type-checking inconsistencies. This can be
+ // removed once we're confident in IR generation results.
+ syntax.Walk(p.file, func(n syntax.Node) bool {
+ g.validate(n)
+ return false
+ })
+ }
+}
+
+func (g *irgen) unhandled(what string, p poser) {
+ base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
+ panic("unreachable")
+}
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 6a0ee831d7..0c7d015977 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -23,48 +23,31 @@ import (
"cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/compile/internal/types2"
"cmd/internal/objabi"
"cmd/internal/src"
)
func LoadPackage(filenames []string) {
base.Timer.Start("fe", "parse")
- lines := ParseFiles(filenames)
- base.Timer.Stop()
- base.Timer.AddEvent(int64(lines), "lines")
- if base.Flag.G != 0 && base.Flag.G < 3 {
- // can only parse generic code for now
- base.ExitIfErrors()
- return
+ mode := syntax.CheckBranches
+ if base.Flag.G != 0 {
+ mode |= syntax.AllowGenerics
}
- // Typecheck.
- Package()
-
- // With all user code typechecked, it's now safe to verify unused dot imports.
- CheckDotImports()
- base.ExitIfErrors()
-}
-
-// ParseFiles concurrently parses files into *syntax.File structures.
-// Each declaration in every *syntax.File is converted to a syntax tree
-// and its root represented by *Node is appended to Target.Decls.
-// Returns the total count of parsed lines.
-func ParseFiles(filenames []string) (lines uint) {
- noders := make([]*noder, 0, len(filenames))
// Limit the number of simultaneously open files.
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
- for _, filename := range filenames {
- p := &noder{
+ noders := make([]*noder, len(filenames))
+ for i, filename := range filenames {
+ p := noder{
err: make(chan syntax.Error),
trackScopes: base.Flag.Dwarf,
}
- noders = append(noders, p)
+ noders[i] = &p
- go func(filename string) {
+ filename := filename
+ go func() {
sem <- struct{}{}
defer func() { <-sem }()
defer close(p.err)
@@ -77,115 +60,42 @@ func ParseFiles(filenames []string) (lines uint) {
}
defer f.Close()
- mode := syntax.CheckBranches
- if base.Flag.G != 0 {
- mode |= syntax.AllowGenerics
- }
p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
- }(filename)
- }
-
- // generic noding phase (using new typechecker)
- if base.Flag.G != 0 {
- // setup and syntax error reporting
- nodersmap := make(map[string]*noder)
- var files []*syntax.File
- for _, p := range noders {
- for e := range p.err {
- p.errorAt(e.Pos, "%s", e.Msg)
- }
-
- nodersmap[p.file.Pos().RelFilename()] = p
- files = append(files, p.file)
- lines += p.file.EOF.Line()
-
- }
- if base.SyntaxErrors() != 0 {
- base.ErrorExit()
- }
-
- // typechecking
- conf := types2.Config{
- InferFromConstraints: true,
- IgnoreBranches: true, // parser already checked via syntax.CheckBranches mode
- CompilerErrorMessages: true, // use error strings matching existing compiler errors
- Error: func(err error) {
- terr := err.(types2.Error)
- if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
- // types2 reports error clarifications via separate
- // error messages which are indented with a tab.
- // Ignore them to satisfy tools and tests that expect
- // only one error in such cases.
- // TODO(gri) Need to adjust error reporting in types2.
- return
- }
- p := nodersmap[terr.Pos.RelFilename()]
- base.ErrorfAt(p.makeXPos(terr.Pos), "%s", terr.Msg)
- },
- Importer: &gcimports{
- packages: make(map[string]*types2.Package),
- },
- Sizes: &gcSizes{},
- }
- info := types2.Info{
- Types: make(map[syntax.Expr]types2.TypeAndValue),
- Defs: make(map[*syntax.Name]types2.Object),
- Uses: make(map[*syntax.Name]types2.Object),
- Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
- // expand as needed
- }
- conf.Check(base.Ctxt.Pkgpath, files, &info)
- base.ExitIfErrors()
- if base.Flag.G < 2 {
- return
- }
-
- // noding
- for _, p := range noders {
- // errors have already been reported
-
- p.typeInfo = &info
- p.node()
- lines += p.file.EOF.Line()
- p.file = nil // release memory
- base.ExitIfErrors()
-
- // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
- types.CheckDclstack()
- }
-
- types.LocalPkg.Height = myheight
- return
+ }()
}
- // traditional (non-generic) noding phase
+ var lines uint
for _, p := range noders {
for e := range p.err {
p.errorAt(e.Pos, "%s", e.Msg)
}
+ lines += p.file.EOF.Line()
+ }
+ base.Timer.AddEvent(int64(lines), "lines")
+
+ if base.Flag.G != 0 {
+ // Use types2 to type-check and possibly generate IR.
+ check2(noders)
+ return
+ }
+ for _, p := range noders {
p.node()
- lines += p.file.EOF.Line()
p.file = nil // release memory
- if base.SyntaxErrors() != 0 {
- base.ErrorExit()
- }
+ }
- // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
- types.CheckDclstack()
+ if base.SyntaxErrors() != 0 {
+ base.ErrorExit()
}
+ types.CheckDclstack()
for _, p := range noders {
p.processPragmas()
}
+ // Typecheck.
types.LocalPkg.Height = myheight
- return
-}
-
-func Package() {
typecheck.DeclareUniverse()
-
typecheck.TypecheckAllowed = true
// Process top-level declarations in phases.
@@ -242,8 +152,10 @@ func Package() {
}
// 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{}) {
@@ -272,37 +184,6 @@ type noder struct {
trackScopes bool
funcState *funcState
-
- // typeInfo provides access to the type information computed by the new
- // typechecker. It is only present if -G is set, and all noders point to
- // the same types.Info. For now this is a local field, if need be we can
- // make it global.
- typeInfo *types2.Info
-}
-
-// For now we provide these basic accessors to get to type and object
-// information of expression nodes during noding. Eventually we will
-// attach this information directly to the syntax tree which should
-// simplify access and make it more efficient as well.
-
-// typ returns the type and value information for the given expression.
-func (p *noder) typ(x syntax.Expr) types2.TypeAndValue {
- return p.typeInfo.Types[x]
-}
-
-// def returns the object for the given name in its declaration.
-func (p *noder) def(x *syntax.Name) types2.Object {
- return p.typeInfo.Defs[x]
-}
-
-// use returns the object for the given name outside its declaration.
-func (p *noder) use(x *syntax.Name) types2.Object {
- return p.typeInfo.Uses[x]
-}
-
-// sel returns the selection information for the given selector expression.
-func (p *noder) sel(x *syntax.SelectorExpr) *types2.Selection {
- return p.typeInfo.Selections[x]
}
// funcState tracks all per-function state to make handling nested
@@ -380,7 +261,6 @@ type linkname struct {
}
func (p *noder) node() {
- types.Block = 1
p.importedUnsafe = false
p.importedEmbed = false
diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go
new file mode 100644
index 0000000000..9567042156
--- /dev/null
+++ b/src/cmd/compile/internal/noder/object.go
@@ -0,0 +1,169 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) {
+ obj, ok := g.info.Defs[name]
+ if !ok {
+ base.FatalfAt(g.pos(name), "unknown name %v", name)
+ }
+ return g.obj(obj), obj
+}
+
+func (g *irgen) use(name *syntax.Name) *ir.Name {
+ obj, ok := g.info.Uses[name]
+ if !ok {
+ base.FatalfAt(g.pos(name), "unknown name %v", name)
+ }
+ return ir.CaptureName(g.pos(obj), ir.CurFunc, g.obj(obj))
+}
+
+// obj returns the Name that represents the given object. If no such
+// Name exists yet, it will be implicitly created.
+//
+// For objects declared at function scope, ir.CurFunc must already be
+// set to the respective function when the Name is created.
+func (g *irgen) obj(obj types2.Object) *ir.Name {
+ // For imported objects, we use iimport directly instead of mapping
+ // the types2 representation.
+ if obj.Pkg() != g.self {
+ sym := g.sym(obj)
+ if sym.Def != nil {
+ return sym.Def.(*ir.Name)
+ }
+ n := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
+ if n, ok := n.(*ir.Name); ok {
+ return n
+ }
+ base.FatalfAt(g.pos(obj), "failed to resolve %v", obj)
+ }
+
+ if name, ok := g.objs[obj]; ok {
+ return name // previously mapped
+ }
+
+ var name *ir.Name
+ pos := g.pos(obj)
+
+ class := typecheck.DeclContext
+ if obj.Parent() == g.self.Scope() {
+ class = ir.PEXTERN // forward reference to package-block declaration
+ }
+
+ // "You are in a maze of twisting little passages, all different."
+ switch obj := obj.(type) {
+ case *types2.Const:
+ name = g.objCommon(pos, ir.OLITERAL, g.sym(obj), class, g.typ(obj.Type()))
+
+ case *types2.Func:
+ sig := obj.Type().(*types2.Signature)
+ var sym *types.Sym
+ var typ *types.Type
+ if recv := sig.Recv(); recv == nil {
+ if obj.Name() == "init" {
+ sym = renameinit()
+ } else {
+ sym = g.sym(obj)
+ }
+ typ = g.typ(sig)
+ } else {
+ sym = ir.MethodSym(g.typ(recv.Type()), g.selector(obj))
+ typ = g.signature(g.param(recv), sig)
+ }
+ name = g.objCommon(pos, ir.ONAME, sym, ir.PFUNC, typ)
+
+ case *types2.TypeName:
+ if obj.IsAlias() {
+ name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type()))
+ } else {
+ name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj))
+ g.objFinish(name, class, types.NewNamed(name))
+ }
+
+ case *types2.Var:
+ var sym *types.Sym
+ if class == ir.PPARAMOUT {
+ // Backend needs names for result parameters,
+ // even if they're anonymous or blank.
+ switch obj.Name() {
+ case "":
+ sym = typecheck.LookupNum("~r", len(ir.CurFunc.Dcl)) // 'r' for "result"
+ case "_":
+ sym = typecheck.LookupNum("~b", len(ir.CurFunc.Dcl)) // 'b' for "blank"
+ }
+ }
+ if sym == nil {
+ sym = g.sym(obj)
+ }
+ name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type()))
+
+ default:
+ g.unhandled("object", obj)
+ }
+
+ g.objs[obj] = name
+ return name
+}
+
+func (g *irgen) objCommon(pos src.XPos, op ir.Op, sym *types.Sym, class ir.Class, typ *types.Type) *ir.Name {
+ name := ir.NewDeclNameAt(pos, op, sym)
+ g.objFinish(name, class, typ)
+ return name
+}
+
+func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) {
+ sym := name.Sym()
+
+ name.SetType(typ)
+ name.Class = class
+ if name.Class == ir.PFUNC {
+ sym.SetFunc(true)
+ }
+
+ // We already know name's type, but typecheck is really eager to try
+ // recomputing it later. This appears to prevent that at least.
+ name.Ntype = ir.TypeNode(typ)
+ name.SetTypecheck(1)
+ name.SetWalkdef(1)
+
+ if ir.IsBlank(name) {
+ return
+ }
+
+ switch class {
+ case ir.PEXTERN:
+ g.target.Externs = append(g.target.Externs, name)
+ fallthrough
+ case ir.PFUNC:
+ sym.Def = name
+ if name.Class == ir.PFUNC && name.Type().Recv() != nil {
+ break // methods are exported with their receiver type
+ }
+ if types.IsExported(sym.Name) {
+ typecheck.Export(name)
+ }
+ if base.Flag.AsmHdr != "" && !name.Sym().Asm() {
+ name.Sym().SetAsm(true)
+ g.target.Asms = append(g.target.Asms, name)
+ }
+
+ default:
+ // Function-scoped declaration.
+ name.Curfn = ir.CurFunc
+ if name.Op() == ir.ONAME {
+ ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, name)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go
index d24f706281..a6d3e2d7ef 100644
--- a/src/cmd/compile/internal/noder/posmap.go
+++ b/src/cmd/compile/internal/noder/posmap.go
@@ -27,7 +27,8 @@ func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) }
func (m *posMap) makeXPos(pos syntax.Pos) src.XPos {
if !pos.IsKnown() {
- base.Fatalf("unknown position")
+ // TODO(mdempsky): Investigate restoring base.Fatalf.
+ return src.NoXPos
}
posBase := m.makeSrcPosBase(pos.Base())
@@ -70,6 +71,9 @@ func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
}
func (m *posMap) join(other *posMap) {
+ if m.bases == nil {
+ m.bases = make(map[*syntax.PosBase]*src.PosBase)
+ }
for k, v := range other.bases {
if m.bases[k] != nil {
base.Fatalf("duplicate posmap bases")
diff --git a/src/cmd/compile/internal/noder/scopes.go b/src/cmd/compile/internal/noder/scopes.go
new file mode 100644
index 0000000000..eb518474c6
--- /dev/null
+++ b/src/cmd/compile/internal/noder/scopes.go
@@ -0,0 +1,64 @@
+// Copyright 2021 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 (
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+)
+
+// recordScopes populates fn.Parents and fn.Marks based on the scoping
+// information provided by types2.
+func (g *irgen) recordScopes(fn *ir.Func, sig *syntax.FuncType) {
+ scope, ok := g.info.Scopes[sig]
+ if !ok {
+ base.FatalfAt(fn.Pos(), "missing scope for %v", fn)
+ }
+
+ for i, n := 0, scope.NumChildren(); i < n; i++ {
+ g.walkScope(scope.Child(i))
+ }
+
+ g.marker.WriteTo(fn)
+}
+
+func (g *irgen) walkScope(scope *types2.Scope) bool {
+ // types2 doesn't provide a proper API for determining the
+ // lexical element a scope represents, so we have to resort to
+ // string matching. Conveniently though, this allows us to
+ // skip both function types and function literals, neither of
+ // which are interesting to us here.
+ if strings.HasPrefix(scope.String(), "function scope ") {
+ return false
+ }
+
+ g.marker.Push(g.pos(scope))
+
+ haveVars := false
+ for _, name := range scope.Names() {
+ if obj, ok := scope.Lookup(name).(*types2.Var); ok && obj.Name() != "_" {
+ haveVars = true
+ break
+ }
+ }
+
+ for i, n := 0, scope.NumChildren(); i < n; i++ {
+ if g.walkScope(scope.Child(i)) {
+ haveVars = true
+ }
+ }
+
+ if haveVars {
+ g.marker.Pop(g.end(scope))
+ } else {
+ g.marker.Unpush()
+ }
+
+ return haveVars
+}
diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go
new file mode 100644
index 0000000000..7d79595a04
--- /dev/null
+++ b/src/cmd/compile/internal/noder/stmt.go
@@ -0,0 +1,280 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
+ var nodes []ir.Node
+ for _, stmt := range stmts {
+ switch s := g.stmt(stmt).(type) {
+ case nil: // EmptyStmt
+ case *ir.BlockStmt:
+ nodes = append(nodes, s.List...)
+ default:
+ nodes = append(nodes, s)
+ }
+ }
+ return nodes
+}
+
+func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
+ // TODO(mdempsky): Remove dependency on typecheck.
+ return typecheck.Stmt(g.stmt0(stmt))
+}
+
+func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
+ switch stmt := stmt.(type) {
+ case nil, *syntax.EmptyStmt:
+ return nil
+ case *syntax.LabeledStmt:
+ return g.labeledStmt(stmt)
+ case *syntax.BlockStmt:
+ return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
+ case *syntax.ExprStmt:
+ x := g.expr(stmt.X)
+ if call, ok := x.(*ir.CallExpr); ok {
+ call.Use = ir.CallUseStmt
+ }
+ return x
+ case *syntax.SendStmt:
+ return ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
+ case *syntax.DeclStmt:
+ return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
+
+ case *syntax.AssignStmt:
+ if stmt.Op != 0 && stmt.Op != syntax.Def {
+ op := g.op(stmt.Op, binOps[:])
+ if stmt.Rhs == syntax.ImplicitOne {
+ return IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
+ }
+ return ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))
+ }
+
+ rhs := g.exprList(stmt.Rhs)
+ if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 {
+ n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, nil, nil)
+ n.Def = stmt.Op == syntax.Def
+ n.Lhs = g.assignList(stmt.Lhs, n, n.Def)
+ n.Rhs = rhs
+ return n
+ }
+
+ n := ir.NewAssignStmt(g.pos(stmt), nil, nil)
+ n.Def = stmt.Op == syntax.Def
+ n.X = g.assignList(stmt.Lhs, n, n.Def)[0]
+ n.Y = rhs[0]
+ return n
+
+ case *syntax.BranchStmt:
+ return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
+ case *syntax.CallStmt:
+ return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
+ case *syntax.ReturnStmt:
+ return ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
+ case *syntax.IfStmt:
+ return g.ifStmt(stmt)
+ case *syntax.ForStmt:
+ return g.forStmt(stmt)
+ case *syntax.SelectStmt:
+ return g.selectStmt(stmt)
+ case *syntax.SwitchStmt:
+ return g.switchStmt(stmt)
+
+ default:
+ g.unhandled("statement", stmt)
+ panic("unreachable")
+ }
+}
+
+// TODO(mdempsky): Investigate replacing with switch statements or dense arrays.
+
+var branchOps = [...]ir.Op{
+ syntax.Break: ir.OBREAK,
+ syntax.Continue: ir.OCONTINUE,
+ syntax.Fallthrough: ir.OFALL,
+ syntax.Goto: ir.OGOTO,
+}
+
+var callOps = [...]ir.Op{
+ syntax.Defer: ir.ODEFER,
+ syntax.Go: ir.OGO,
+}
+
+func (g *irgen) tokOp(tok int, ops []ir.Op) ir.Op {
+ // TODO(mdempsky): Validate.
+ return ops[tok]
+}
+
+func (g *irgen) op(op syntax.Operator, ops []ir.Op) ir.Op {
+ // TODO(mdempsky): Validate.
+ return ops[op]
+}
+
+func (g *irgen) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node {
+ if !colas {
+ return g.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))
+ for i, expr := range exprs {
+ expr := expr.(*syntax.Name)
+ if expr.Value == "_" {
+ res[i] = ir.BlankNode
+ continue
+ }
+
+ if obj, ok := g.info.Uses[expr]; ok {
+ res[i] = g.obj(obj)
+ continue
+ }
+
+ name, _ := g.def(expr)
+ name.Defn = defn
+ defn.PtrInit().Append(ir.NewDecl(name.Pos(), ir.ODCL, name))
+ res[i] = name
+ }
+ return res
+}
+
+func (g *irgen) blockStmt(stmt *syntax.BlockStmt) []ir.Node {
+ return g.stmts(stmt.List)
+}
+
+func (g *irgen) ifStmt(stmt *syntax.IfStmt) ir.Node {
+ init := g.stmt(stmt.Init)
+ n := ir.NewIfStmt(g.pos(stmt), g.expr(stmt.Cond), g.blockStmt(stmt.Then), nil)
+ if stmt.Else != nil {
+ e := g.stmt(stmt.Else)
+ if e.Op() == ir.OBLOCK {
+ e := e.(*ir.BlockStmt)
+ n.Else = e.List
+ } else {
+ n.Else = []ir.Node{e}
+ }
+ }
+ return g.init(init, n)
+}
+
+func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node {
+ if r, ok := stmt.Init.(*syntax.RangeClause); ok {
+ n := ir.NewRangeStmt(g.pos(r), nil, nil, g.expr(r.X), nil)
+ if r.Lhs != nil {
+ n.Def = r.Def
+ lhs := g.assignList(r.Lhs, n, n.Def)
+ n.Key = lhs[0]
+ if len(lhs) > 1 {
+ n.Value = lhs[1]
+ }
+ }
+ n.Body = g.blockStmt(stmt.Body)
+ return n
+ }
+
+ return ir.NewForStmt(g.pos(stmt), g.stmt(stmt.Init), g.expr(stmt.Cond), g.stmt(stmt.Post), g.blockStmt(stmt.Body))
+}
+
+func (g *irgen) selectStmt(stmt *syntax.SelectStmt) ir.Node {
+ body := make([]*ir.CommClause, len(stmt.Body))
+ for i, clause := range stmt.Body {
+ body[i] = ir.NewCommStmt(g.pos(clause), g.stmt(clause.Comm), g.stmts(clause.Body))
+ }
+ return ir.NewSelectStmt(g.pos(stmt), body)
+}
+
+func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
+ pos := g.pos(stmt)
+ init := g.stmt(stmt.Init)
+
+ var expr ir.Node
+ switch tag := stmt.Tag.(type) {
+ case *syntax.TypeSwitchGuard:
+ var ident *ir.Ident
+ if tag.Lhs != nil {
+ ident = ir.NewIdent(g.pos(tag.Lhs), g.name(tag.Lhs))
+ }
+ expr = ir.NewTypeSwitchGuard(pos, ident, g.expr(tag.X))
+ default:
+ expr = g.expr(tag)
+ }
+
+ body := make([]*ir.CaseClause, len(stmt.Body))
+ for i, clause := range stmt.Body {
+ // Check for an implicit clause variable before
+ // visiting body, because it may contain function
+ // literals that reference it, and then it'll be
+ // associated to the wrong function.
+ //
+ // Also, override its position to the clause's colon, so that
+ // dwarfgen can find the right scope for it later.
+ // TODO(mdempsky): We should probably just store the scope
+ // directly in the ir.Name.
+ var cv *ir.Name
+ if obj, ok := g.info.Implicits[clause]; ok {
+ cv = g.obj(obj)
+ cv.SetPos(g.makeXPos(clause.Colon))
+ }
+ body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body))
+ body[i].Var = cv
+ }
+
+ return g.init(init, ir.NewSwitchStmt(pos, expr, body))
+}
+
+func (g *irgen) labeledStmt(label *syntax.LabeledStmt) ir.Node {
+ sym := g.name(label.Label)
+ lhs := ir.NewLabelStmt(g.pos(label), sym)
+ ls := g.stmt(label.Stmt)
+
+ // Attach label directly to control statement too.
+ switch ls := ls.(type) {
+ case *ir.ForStmt:
+ ls.Label = sym
+ case *ir.RangeStmt:
+ ls.Label = sym
+ case *ir.SelectStmt:
+ ls.Label = sym
+ case *ir.SwitchStmt:
+ 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)
+}
+
+func (g *irgen) init(init ir.Node, stmt ir.InitNode) ir.InitNode {
+ if init != nil {
+ stmt.SetInit([]ir.Node{init})
+ }
+ return stmt
+}
+
+func (g *irgen) name(name *syntax.Name) *types.Sym {
+ if name == nil {
+ return nil
+ }
+ return typecheck.Lookup(name.Value)
+}
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
new file mode 100644
index 0000000000..0635d76077
--- /dev/null
+++ b/src/cmd/compile/internal/noder/types.go
@@ -0,0 +1,208 @@
+// Copyright 2021 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 (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
+ switch pkg {
+ case nil:
+ return types.BuiltinPkg
+ case g.self:
+ return types.LocalPkg
+ case types2.Unsafe:
+ return ir.Pkgs.Unsafe
+ }
+ return types.NewPkg(pkg.Path(), pkg.Name())
+}
+
+func (g *irgen) typ(typ types2.Type) *types.Type {
+ switch typ := typ.(type) {
+ case *types2.Basic:
+ return g.basic(typ)
+ case *types2.Named:
+ obj := g.obj(typ.Obj())
+ if obj.Op() != ir.OTYPE {
+ base.FatalfAt(obj.Pos(), "expected type: %L", obj)
+ }
+ return obj.Type()
+
+ case *types2.Array:
+ return types.NewArray(g.typ(typ.Elem()), typ.Len())
+ case *types2.Chan:
+ return types.NewChan(g.typ(typ.Elem()), dirs[typ.Dir()])
+ case *types2.Map:
+ return types.NewMap(g.typ(typ.Key()), g.typ(typ.Elem()))
+ case *types2.Pointer:
+ return types.NewPtr(g.typ(typ.Elem()))
+ case *types2.Signature:
+ return g.signature(nil, typ)
+ case *types2.Slice:
+ return types.NewSlice(g.typ(typ.Elem()))
+
+ case *types2.Struct:
+ fields := make([]*types.Field, typ.NumFields())
+ for i := range fields {
+ v := typ.Field(i)
+ f := types.NewField(g.pos(v), g.selector(v), g.typ(v.Type()))
+ f.Note = typ.Tag(i)
+ if v.Embedded() {
+ f.Embedded = 1
+ }
+ fields[i] = f
+ }
+ return types.NewStruct(g.tpkg(typ), fields)
+
+ case *types2.Interface:
+ embeddeds := make([]*types.Field, typ.NumEmbeddeds())
+ for i := range embeddeds {
+ // TODO(mdempsky): Get embedding position.
+ e := typ.EmbeddedType(i)
+ embeddeds[i] = types.NewField(src.NoXPos, nil, g.typ(e))
+ }
+
+ methods := make([]*types.Field, typ.NumExplicitMethods())
+ for i := range methods {
+ m := typ.ExplicitMethod(i)
+ mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature))
+ methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
+ }
+
+ return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...))
+
+ default:
+ base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ)
+ panic("unreachable")
+ }
+}
+
+func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
+ do := func(typ *types2.Tuple) []*types.Field {
+ fields := make([]*types.Field, typ.Len())
+ for i := range fields {
+ fields[i] = g.param(typ.At(i))
+ }
+ return fields
+ }
+
+ params := do(sig.Params())
+ results := do(sig.Results())
+ if sig.Variadic() {
+ params[len(params)-1].SetIsDDD(true)
+ }
+
+ return types.NewSignature(g.tpkg(sig), recv, params, results)
+}
+
+func (g *irgen) param(v *types2.Var) *types.Field {
+ return types.NewField(g.pos(v), g.sym(v), g.typ(v.Type()))
+}
+
+func (g *irgen) sym(obj types2.Object) *types.Sym {
+ if name := obj.Name(); name != "" {
+ return g.pkg(obj.Pkg()).Lookup(obj.Name())
+ }
+ return nil
+}
+
+func (g *irgen) selector(obj types2.Object) *types.Sym {
+ pkg, name := g.pkg(obj.Pkg()), obj.Name()
+ if types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ return pkg.Lookup(name)
+}
+
+// tpkg returns the package that a function, interface, or struct type
+// expression appeared in.
+//
+// Caveat: For the degenerate types "func()", "interface{}", and
+// "struct{}", tpkg always returns LocalPkg. However, we only need the
+// package information so that go/types can report it via its API, and
+// the reason we fail to return the original package for these
+// particular types is because go/types does *not* report it for
+// them. So in practice this limitation is probably moot.
+func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
+ anyObj := func() types2.Object {
+ switch typ := typ.(type) {
+ case *types2.Signature:
+ if recv := typ.Recv(); recv != nil {
+ return recv
+ }
+ if params := typ.Params(); params.Len() > 0 {
+ return params.At(0)
+ }
+ if results := typ.Results(); results.Len() > 0 {
+ return results.At(0)
+ }
+ case *types2.Struct:
+ if typ.NumFields() > 0 {
+ return typ.Field(0)
+ }
+ case *types2.Interface:
+ if typ.NumExplicitMethods() > 0 {
+ return typ.ExplicitMethod(0)
+ }
+ }
+ return nil
+ }
+
+ if obj := anyObj(); obj != nil {
+ return g.pkg(obj.Pkg())
+ }
+ return types.LocalPkg
+}
+
+func (g *irgen) basic(typ *types2.Basic) *types.Type {
+ switch typ.Name() {
+ case "byte":
+ return types.ByteType
+ case "rune":
+ return types.RuneType
+ }
+ return *basics[typ.Kind()]
+}
+
+var basics = [...]**types.Type{
+ types2.Invalid: new(*types.Type),
+ types2.Bool: &types.Types[types.TBOOL],
+ types2.Int: &types.Types[types.TINT],
+ types2.Int8: &types.Types[types.TINT8],
+ types2.Int16: &types.Types[types.TINT16],
+ types2.Int32: &types.Types[types.TINT32],
+ types2.Int64: &types.Types[types.TINT64],
+ types2.Uint: &types.Types[types.TUINT],
+ types2.Uint8: &types.Types[types.TUINT8],
+ types2.Uint16: &types.Types[types.TUINT16],
+ types2.Uint32: &types.Types[types.TUINT32],
+ types2.Uint64: &types.Types[types.TUINT64],
+ types2.Uintptr: &types.Types[types.TUINTPTR],
+ types2.Float32: &types.Types[types.TFLOAT32],
+ types2.Float64: &types.Types[types.TFLOAT64],
+ types2.Complex64: &types.Types[types.TCOMPLEX64],
+ types2.Complex128: &types.Types[types.TCOMPLEX128],
+ types2.String: &types.Types[types.TSTRING],
+ types2.UnsafePointer: &types.Types[types.TUNSAFEPTR],
+ types2.UntypedBool: &types.UntypedBool,
+ types2.UntypedInt: &types.UntypedInt,
+ types2.UntypedRune: &types.UntypedRune,
+ types2.UntypedFloat: &types.UntypedFloat,
+ types2.UntypedComplex: &types.UntypedComplex,
+ types2.UntypedString: &types.UntypedString,
+ types2.UntypedNil: &types.Types[types.TNIL],
+}
+
+var dirs = [...]types.ChanDir{
+ types2.SendRecv: types.Cboth,
+ types2.SendOnly: types.Csend,
+ types2.RecvOnly: types.Crecv,
+}
diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go
new file mode 100644
index 0000000000..f97f81d5ad
--- /dev/null
+++ b/src/cmd/compile/internal/noder/validate.go
@@ -0,0 +1,113 @@
+// Copyright 2021 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 (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+)
+
+// match reports whether types t1 and t2 are consistent
+// representations for a given expression's type.
+func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
+ tuple, ok := t2.(*types2.Tuple)
+ if !ok {
+ // Not a tuple; can use simple type identity comparison.
+ return types.Identical(t1, g.typ(t2))
+ }
+
+ if hasOK {
+ // For has-ok values, types2 represents the expression's type as
+ // a 2-element tuple, whereas ir just uses the first type and
+ // infers that the second type is boolean.
+ return tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type()))
+ }
+
+ if t1 == nil || tuple == nil {
+ return t1 == nil && tuple == nil
+ }
+ if !t1.IsFuncArgStruct() {
+ return false
+ }
+ if t1.NumFields() != tuple.Len() {
+ return false
+ }
+ for i, result := range t1.FieldSlice() {
+ if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) {
+ return false
+ }
+ }
+ return true
+}
+
+func (g *irgen) validate(n syntax.Node) {
+ switch n := n.(type) {
+ case *syntax.CallExpr:
+ tv := g.info.Types[n.Fun]
+ if tv.IsBuiltin() {
+ switch builtin := n.Fun.(type) {
+ case *syntax.Name:
+ g.validateBuiltin(builtin.Value, n)
+ case *syntax.SelectorExpr:
+ g.validateBuiltin(builtin.Sel.Value, n)
+ default:
+ g.unhandled("builtin", n)
+ }
+ }
+ }
+}
+
+func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
+ switch name {
+ case "Alignof", "Offsetof", "Sizeof":
+ // Check that types2+gcSizes calculates sizes the same
+ // as cmd/compile does.
+
+ got, ok := constant.Int64Val(g.info.Types[call].Value)
+ if !ok {
+ base.FatalfAt(g.pos(call), "expected int64 constant value")
+ }
+
+ want := g.unsafeExpr(name, call.ArgList[0])
+ if got != want {
+ base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want)
+ }
+ }
+}
+
+// unsafeExpr evaluates the given unsafe builtin function on arg.
+func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 {
+ switch name {
+ case "Alignof":
+ return g.typ(g.info.Types[arg].Type).Alignment()
+ case "Sizeof":
+ return g.typ(g.info.Types[arg].Type).Size()
+ }
+
+ // Offsetof
+
+ sel := arg.(*syntax.SelectorExpr)
+ selection := g.info.Selections[sel]
+
+ typ := g.typ(g.info.Types[sel.X].Type)
+ if typ.IsPtr() {
+ typ = typ.Elem()
+ }
+
+ var offset int64
+ for _, i := range selection.Index() {
+ // Ensure field offsets have been calculated.
+ types.CalcSize(typ)
+
+ f := typ.Field(i)
+ offset += f.Offset
+ typ = f.Type
+ }
+ return offset
+}
diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go
index c7d7506fd1..bd54919c93 100644
--- a/src/cmd/compile/internal/typecheck/dcl.go
+++ b/src/cmd/compile/internal/typecheck/dcl.go
@@ -304,10 +304,13 @@ func checkembeddedtype(t *types.Type) {
}
}
-func fakeRecvField() *types.Field {
+// TODO(mdempsky): Move to package types.
+func FakeRecv() *types.Field {
return types.NewField(src.NoXPos, nil, types.FakeRecvType())
}
+var fakeRecvField = FakeRecv
+
var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext
type funcStackEnt struct {
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index 03a10f594a..766eb8bae9 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -174,7 +174,7 @@ func fnpkg(fn *ir.Name) *types.Pkg {
// closurename generates a new unique name for a closure within
// outerfunc.
-func closurename(outerfunc *ir.Func) *types.Sym {
+func ClosureName(outerfunc *ir.Func) *types.Sym {
outer := "glob."
prefix := "func"
gen := &globClosgen
@@ -303,7 +303,7 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
return
}
- fn.Nname.SetSym(closurename(ir.CurFunc))
+ fn.Nname.SetSym(ClosureName(ir.CurFunc))
ir.MarkFunc(fn.Nname)
Func(fn)
clo.SetType(fn.Type())
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 814af59772..d5100021a2 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -1674,10 +1674,10 @@ func CheckMapKeys() {
mapqueue = nil
}
-// typegen tracks the number of function-scoped defined types that
+// TypeGen tracks the number of function-scoped defined types that
// have been declared. It's used to generate unique linker symbols for
// their runtime type descriptors.
-var typegen int32
+var TypeGen int32
func typecheckdeftype(n *ir.Name) {
if base.EnableTrace && base.Flag.LowerT {
@@ -1686,8 +1686,8 @@ func typecheckdeftype(n *ir.Name) {
t := types.NewNamed(n)
if n.Curfn != nil {
- typegen++
- t.Vargen = typegen
+ TypeGen++
+ t.Vargen = TypeGen
}
if n.Pragma()&ir.NotInHeap != 0 {
diff --git a/src/cmd/compile/internal/types/scope.go b/src/cmd/compile/internal/types/scope.go
index a9669ffafc..d7c454f379 100644
--- a/src/cmd/compile/internal/types/scope.go
+++ b/src/cmd/compile/internal/types/scope.go
@@ -12,7 +12,7 @@ import (
// Declaration stack & operations
var blockgen int32 = 1 // max block number
-var Block int32 // current block number
+var Block int32 = 1 // current block number
// A dsym stores a symbol's shadowed declaration so that it can be
// restored once the block scope ends.