summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/noder/decl.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/decl.go')
-rw-r--r--src/cmd/compile/internal/noder/decl.go352
1 files changed, 352 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
new file mode 100644
index 0000000..07353cc
--- /dev/null
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -0,0 +1,352 @@
+// 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/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(res *ir.Nodes, decls []syntax.Decl) {
+ 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)
+ }
+ }
+}
+
+func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
+ g.pragmaFlags(decl.Pragma, 0)
+
+ // Get the imported package's path, as resolved already by types2
+ // and gcimporter. This is the same path as would be computed by
+ // parseImportPath.
+ switch pkgNameOf(g.info, decl).Imported().Path() {
+ case "unsafe":
+ p.importedUnsafe = true
+ case "embed":
+ p.importedEmbed = true
+ }
+}
+
+// pkgNameOf returns the PkgName associated with the given ImportDecl.
+func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName {
+ if name := decl.LocalPkgName; name != nil {
+ return info.Defs[name].(*types2.PkgName)
+ }
+ return info.Implicits[decl].(*types2.PkgName)
+}
+
+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)
+
+ // For untyped numeric constants, make sure the value
+ // representation matches what the rest of the
+ // compiler (really just iexport) expects.
+ // TODO(mdempsky): Revisit after #43891 is resolved.
+ val := obj.(*types2.Const).Val()
+ switch name.Type() {
+ case types.UntypedInt, types.UntypedRune:
+ val = constant.ToInt(val)
+ case types.UntypedFloat:
+ val = constant.ToFloat(val)
+ case types.UntypedComplex:
+ val = constant.ToComplex(val)
+ }
+ name.SetVal(val)
+
+ out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name))
+ }
+}
+
+func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
+ assert(g.curDecl == "")
+ // Set g.curDecl to the function name, as context for the type params declared
+ // during types2-to-types1 translation if this is a generic function.
+ g.curDecl = decl.Name.Value
+ obj2 := g.info.Defs[decl.Name]
+ recv := types2.AsSignature(obj2.Type()).Recv()
+ if recv != nil {
+ t2 := deref2(recv.Type())
+ // This is a method, so set g.curDecl to recvTypeName.methName instead.
+ g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl
+ }
+
+ 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 fn.Pragma&ir.Nointerface != 0 {
+ // Propagate //go:nointerface from Func.Pragma to Field.Nointerface.
+ // This is a bit roundabout, but this is the earliest point where we've
+ // processed the function's pragma flags, and we've also already created
+ // the Fields to represent the receiver's method set.
+ if recv := fn.Type().Recv(); recv != nil {
+ typ := types.ReceiverBaseType(recv.Type)
+ if orig := typ.OrigType(); orig != nil {
+ // For a generic method, we mark the methods on the
+ // base generic type, since those are the methods
+ // that will be stenciled.
+ typ = orig
+ }
+ meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0)
+ meth.SetNointerface(true)
+ }
+ }
+
+ if decl.Body != nil {
+ if fn.Pragma&ir.Noescape != 0 {
+ base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations")
+ }
+ if (fn.Pragma&ir.UintptrKeepAlive != 0 && fn.Pragma&ir.UintptrEscapes == 0) && fn.Pragma&ir.Nosplit == 0 {
+ // Stack growth can't handle uintptr arguments that may
+ // be pointers (as we don't know which are pointers
+ // when creating the stack map). Thus uintptrkeepalive
+ // functions (and all transitive callees) must be
+ // nosplit.
+ //
+ // N.B. uintptrescapes implies uintptrkeepalive but it
+ // is OK since the arguments must escape to the heap.
+ //
+ // TODO(prattmic): Add recursive nosplit check of callees.
+ // TODO(prattmic): Functions with no body (i.e.,
+ // assembly) must also be nosplit, but we can't check
+ // that here.
+ base.ErrorfAt(fn.Pos(), "go:uintptrkeepalive requires go:nosplit")
+ }
+ }
+
+ if decl.Name.Value == "init" && decl.Recv == nil {
+ g.target.Inits = append(g.target.Inits, fn)
+ }
+
+ saveHaveEmbed := g.haveEmbed
+ saveCurDecl := g.curDecl
+ g.curDecl = ""
+ g.later(func() {
+ defer func(b bool, s string) {
+ // Revert haveEmbed and curDecl back to what they were before
+ // the "later" function.
+ g.haveEmbed = b
+ g.curDecl = s
+ }(g.haveEmbed, g.curDecl)
+
+ // Set haveEmbed and curDecl to what they were for this funcDecl.
+ g.haveEmbed = saveHaveEmbed
+ g.curDecl = saveCurDecl
+ if fn.Type().HasTParam() {
+ g.topFuncIsGeneric = true
+ }
+ g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
+ g.topFuncIsGeneric = false
+ if fn.Type().HasTParam() && fn.Body != nil {
+ // Set pointers to the dcls/body of a generic function/method in
+ // the Inl struct, so it is marked for export, is available for
+ // stenciling, and works with Inline_Flood().
+ fn.Inl = &ir.Inline{
+ Cost: 1,
+ Dcl: fn.Dcl,
+ Body: fn.Body,
+ }
+ }
+
+ out.Append(fn)
+ })
+}
+
+func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = g.pos(decl)
+ assert(ir.CurFunc != nil || g.curDecl == "")
+ // Set g.curDecl to the type name, as context for the type params declared
+ // during types2-to-types1 translation if this is a generic type.
+ saveCurDecl := g.curDecl
+ g.curDecl = decl.Name.Value
+ if decl.Alias {
+ name, _ := g.def(decl.Name)
+ g.pragmaFlags(decl.Pragma, 0)
+ assert(name.Alias()) // should be set by irgen.obj
+
+ out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
+ g.curDecl = ""
+ return
+ }
+
+ // Prevent size calculations until we set the underlying type.
+ types.DeferCheckSize()
+
+ name, obj := g.def(decl.Name)
+ ntyp, otyp := name.Type(), obj.Type()
+ if ir.CurFunc != nil {
+ ntyp.SetVargen()
+ }
+
+ pragmas := g.pragmaFlags(decl.Pragma, 0)
+ name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed?
+
+ ntyp.SetUnderlying(g.typeExpr(decl.Type))
+
+ tparams := otyp.(*types2.Named).TypeParams()
+ if n := tparams.Len(); n > 0 {
+ rparams := make([]*types.Type, n)
+ for i := range rparams {
+ rparams[i] = g.typ(tparams.At(i))
+ }
+ // This will set hasTParam flag if any rparams are not concrete types.
+ ntyp.SetRParams(rparams)
+ }
+ types.ResumeCheckSize()
+
+ g.curDecl = saveCurDecl
+ if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
+ methods := make([]*types.Field, otyp.NumMethods())
+ for i := range methods {
+ m := otyp.Method(i)
+ // Set g.curDecl to recvTypeName.methName, as context for the
+ // method-specific type params in the receiver.
+ g.curDecl = decl.Name.Value + "." + m.Name()
+ meth := g.obj(m)
+ methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
+ methods[i].Nname = meth
+ g.curDecl = ""
+ }
+ 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)
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = pos
+ names := make([]*ir.Name, len(decl.NameList))
+ for i, name := range decl.NameList {
+ names[i], _ = g.def(name)
+ }
+
+ if decl.Pragma != nil {
+ pragma := decl.Pragma.(*pragmas)
+ varEmbed(g.makeXPos, names[0], decl, pragma, g.haveEmbed)
+ g.reportUnused(pragma)
+ }
+
+ haveEmbed := g.haveEmbed
+ do := func() {
+ defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
+
+ g.haveEmbed = haveEmbed
+ values := g.exprList(decl.Values)
+
+ 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
+ }
+ if !g.delayTransform() {
+ lhs := []ir.Node{as.X}
+ rhs := []ir.Node{}
+ if as.Y != nil {
+ rhs = []ir.Node{as.Y}
+ }
+ transformAssign(as, lhs, rhs)
+ as.X = lhs[0]
+ if as.Y != nil {
+ as.Y = rhs[0]
+ }
+ }
+ as.SetTypecheck(1)
+ out.Append(as)
+ }
+ }
+ if as2 != nil {
+ if !g.delayTransform() {
+ transformAssign(as2, as2.Lhs, as2.Rhs)
+ }
+ as2.SetTypecheck(1)
+ out.Append(as2)
+ }
+ }
+
+ // If we're within a function, we need to process the assignment
+ // part of the variable declaration right away. Otherwise, we leave
+ // it to be handled after all top-level declarations are processed.
+ if ir.CurFunc != nil {
+ do()
+ } else {
+ g.later(do)
+ }
+}
+
+// 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")
+ }
+ }
+}