// 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) { 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 typ.OrigSym != nil { // For a generic method, we mark the methods on the // base generic type, since those are the methods // that will be stenciled. typ = typ.OrigSym.Def.Type() } meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0) meth.SetNointerface(true) } } if decl.Name.Value == "init" && decl.Recv == nil { g.target.Inits = append(g.target.Inits, fn) } g.later(func() { 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) { 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)) 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, 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 { … } // // 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)) tparams := otyp.(*types2.Named).TParams() 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() 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) } if decl.Pragma != nil { pragma := decl.Pragma.(*pragmas) // TODO(mdempsky): Plumb noder.importedEmbed through to here. varEmbed(g.makeXPos, names[0], decl, pragma, true) g.reportUnused(pragma) } do := func() { 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 } 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 { 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") } } }