diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/decl.go')
-rw-r--r-- | src/cmd/compile/internal/noder/decl.go | 352 |
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") + } + } +} |