mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
223 lines
5.8 KiB
Go
223 lines
5.8 KiB
Go
|
|
// 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")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|