blob: f0cdcbfc2e05ca015721af25f52d6255666390a3 [file] [log] [blame]
// 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 { … }
//
// 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 len(decl.TParamList) > 0 {
// Set HasTParam if there are any tparams, even if no tparams are
// used in the type itself (e.g., if it is an empty struct, or no
// fields in the struct use the tparam).
ntyp.SetHasTParam(true)
}
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")
}
}
}