mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Closures inside generic functions were being added to the g.target.Decls list during noding, just like other closures. We remove generic functions/methods from g.target.Decls, so they don't get compiled (they're only available for export and stenciling). Most closures inside generic functions/methods were similarly being removed from g.target.Decls, because they have a generic parameter. But we need to ensure no closures in generic function/methods are left remaining in g.target.Decls, since we don't want them transformed and compiled. So, we set a flag in (*irgen) that records when we are noding a top-level generic function/method, and don't add any closures to g.target.Decls when the flag is true. Updates #47514 Change-Id: Id66b4c41d307ffa8f54cab6ce3646ade81606862 Reviewed-on: https://go-review.googlesource.com/c/go/+/340258 Trust: Dan Scales <danscales@google.com> Reviewed-by: Keith Randall <khr@golang.org>
279 lines
7.3 KiB
Go
279 lines
7.3 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 (
|
|
"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) {
|
|
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 decl.Name.Value == "init" && decl.Recv == nil {
|
|
g.target.Inits = append(g.target.Inits, fn)
|
|
}
|
|
|
|
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 {
|
|
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 { … }
|
|
//
|
|
// 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).Type())
|
|
}
|
|
// 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)
|
|
}
|
|
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
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
}
|
|
}
|