| // 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(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 | |
| } | |
| if ipkg.Path == "embed" { | |
| p.importedEmbed = 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) | |
| // 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 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 { | |
| 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 | |
| } | |
| // 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 { | |
| 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)) | |
| 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) | |
| } | |
| values := g.exprList(decl.Values) | |
| 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) | |
| } | |
| 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") | |
| } | |
| } | |
| } |