blob: 029c14f81924cf58d8435d313bbe108b7112a9a0 [file] [log] [blame]
// Copyright 2009 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 typecheck
import (
"fmt"
"internal/types/errors"
"sync"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/src"
)
var DeclContext ir.Class = ir.PEXTERN // PEXTERN/PAUTO
func DeclFunc(sym *types.Sym, recv *ir.Field, params, results []*ir.Field) *ir.Func {
fn := ir.NewFunc(base.Pos)
fn.Nname = ir.NewNameAt(base.Pos, sym)
fn.Nname.Func = fn
fn.Nname.Defn = fn
ir.MarkFunc(fn.Nname)
StartFuncBody(fn)
var recv1 *types.Field
if recv != nil {
recv1 = declareParam(ir.PPARAM, -1, recv)
}
typ := types.NewSignature(recv1, declareParams(ir.PPARAM, params), declareParams(ir.PPARAMOUT, results))
checkdupfields("argument", typ.Recvs().FieldSlice(), typ.Params().FieldSlice(), typ.Results().FieldSlice())
fn.Nname.SetType(typ)
fn.Nname.SetTypecheck(1)
return fn
}
// Declare records that Node n declares symbol n.Sym in the specified
// declaration context.
func Declare(n *ir.Name, ctxt ir.Class) {
if ir.IsBlank(n) {
return
}
s := n.Sym()
// kludgy: TypecheckAllowed means we're past parsing. Eg reflectdata.methodWrapper may declare out of package names later.
if !inimport && !TypecheckAllowed && s.Pkg != types.LocalPkg {
base.ErrorfAt(n.Pos(), 0, "cannot declare name %v", s)
}
if ctxt == ir.PEXTERN {
if s.Name == "init" {
base.ErrorfAt(n.Pos(), errors.InvalidInitDecl, "cannot declare init - must be func")
}
if s.Name == "main" && s.Pkg.Name == "main" {
base.ErrorfAt(n.Pos(), errors.InvalidMainDecl, "cannot declare main - must be func")
}
Target.Externs = append(Target.Externs, n)
s.Def = n
} else {
if ir.CurFunc == nil && ctxt == ir.PAUTO {
base.Pos = n.Pos()
base.Fatalf("automatic outside function")
}
if ir.CurFunc != nil && ctxt != ir.PFUNC && n.Op() == ir.ONAME {
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
}
n.Curfn = ir.CurFunc
}
if ctxt == ir.PAUTO {
n.SetFrameOffset(0)
}
n.Class = ctxt
if ctxt == ir.PFUNC {
n.Sym().SetFunc(true)
}
autoexport(n, ctxt)
}
// Export marks n for export (or reexport).
func Export(n *ir.Name) {
if n.Sym().OnExportList() {
return
}
n.Sym().SetOnExportList(true)
if base.Flag.E != 0 {
fmt.Printf("export symbol %v\n", n.Sym())
}
Target.Exports = append(Target.Exports, n)
}
// declare the function proper
// and declare the arguments.
// called in extern-declaration context
// returns in auto-declaration context.
func StartFuncBody(fn *ir.Func) {
// change the declaration context from extern to auto
funcStack = append(funcStack, funcStackEnt{ir.CurFunc, DeclContext})
ir.CurFunc = fn
DeclContext = ir.PAUTO
}
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
func FinishFuncBody() {
// change the declaration context from auto to previous context
var e funcStackEnt
funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
ir.CurFunc, DeclContext = e.curfn, e.dclcontext
}
func CheckFuncStack() {
if len(funcStack) != 0 {
base.Fatalf("funcStack is non-empty: %v", len(funcStack))
}
}
func autoexport(n *ir.Name, ctxt ir.Class) {
if n.Sym().Pkg != types.LocalPkg {
return
}
if (ctxt != ir.PEXTERN && ctxt != ir.PFUNC) || DeclContext != ir.PEXTERN {
return
}
if n.Type() != nil && n.Type().IsKind(types.TFUNC) && ir.IsMethod(n) {
return
}
if types.IsExported(n.Sym().Name) || n.Sym().Name == "init" {
Export(n)
}
if base.Flag.AsmHdr != "" && !n.Sym().Asm() {
n.Sym().SetAsm(true)
Target.Asms = append(Target.Asms, n)
}
}
// checkdupfields emits errors for duplicately named fields or methods in
// a list of struct or interface types.
func checkdupfields(what string, fss ...[]*types.Field) {
seen := make(map[*types.Sym]bool)
for _, fs := range fss {
for _, f := range fs {
if f.Sym == nil || f.Sym.IsBlank() {
continue
}
if seen[f.Sym] {
base.ErrorfAt(f.Pos, errors.DuplicateFieldAndMethod, "duplicate %s %s", what, f.Sym.Name)
continue
}
seen[f.Sym] = true
}
}
}
// structs, functions, and methods.
// they don't belong here, but where do they belong?
func checkembeddedtype(t *types.Type) {
if t == nil {
return
}
if t.Sym() == nil && t.IsPtr() {
t = t.Elem()
if t.IsInterface() {
base.Errorf("embedded type cannot be a pointer to interface")
}
}
if t.IsPtr() || t.IsUnsafePtr() {
base.Errorf("embedded type cannot be a pointer")
} else if t.Kind() == types.TFORW && !t.ForwardType().Embedlineno.IsKnown() {
t.ForwardType().Embedlineno = base.Pos
}
}
var funcStack []funcStackEnt // stack of previous values of ir.CurFunc/DeclContext
type funcStackEnt struct {
curfn *ir.Func
dclcontext ir.Class
}
func declareParams(ctxt ir.Class, l []*ir.Field) []*types.Field {
fields := make([]*types.Field, len(l))
for i, n := range l {
fields[i] = declareParam(ctxt, i, n)
}
return fields
}
func declareParam(ctxt ir.Class, i int, param *ir.Field) *types.Field {
f := types.NewField(param.Pos, param.Sym, param.Type)
f.SetIsDDD(param.IsDDD)
sym := param.Sym
if ctxt == ir.PPARAMOUT {
if sym == nil {
// Name so that escape analysis can track it. ~r stands for 'result'.
sym = LookupNum("~r", i)
} else if sym.IsBlank() {
// Give it a name so we can assign to it during return. ~b stands for 'blank'.
// The name must be different from ~r above because if you have
// func f() (_ int)
// func g() int
// f is allowed to use a plain 'return' with no arguments, while g is not.
// So the two cases must be distinguished.
sym = LookupNum("~b", i)
}
}
if sym != nil {
name := ir.NewNameAt(param.Pos, sym)
name.SetType(f.Type)
name.SetTypecheck(1)
Declare(name, ctxt)
f.Nname = name
}
return f
}
func Temp(t *types.Type) *ir.Name {
return TempAt(base.Pos, ir.CurFunc, t)
}
// make a new Node off the books.
func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name {
if curfn == nil {
base.Fatalf("no curfn for TempAt")
}
if curfn.Op() == ir.OCLOSURE {
ir.Dump("TempAt", curfn)
base.Fatalf("adding TempAt to wrong closure function")
}
if t == nil {
base.Fatalf("TempAt called with nil type")
}
if t.Kind() == types.TFUNC && t.Recv() != nil {
base.Fatalf("misuse of method type: %v", t)
}
s := &types.Sym{
Name: autotmpname(len(curfn.Dcl)),
Pkg: types.LocalPkg,
}
n := ir.NewNameAt(pos, s)
s.Def = n
n.SetType(t)
n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetEsc(ir.EscNever)
n.Curfn = curfn
n.SetUsed(true)
n.SetAutoTemp(true)
curfn.Dcl = append(curfn.Dcl, n)
types.CalcSize(t)
return n
}
var (
autotmpnamesmu sync.Mutex
autotmpnames []string
)
// autotmpname returns the name for an autotmp variable numbered n.
func autotmpname(n int) string {
autotmpnamesmu.Lock()
defer autotmpnamesmu.Unlock()
// Grow autotmpnames, if needed.
if n >= len(autotmpnames) {
autotmpnames = append(autotmpnames, make([]string, n+1-len(autotmpnames))...)
autotmpnames = autotmpnames[:cap(autotmpnames)]
}
s := autotmpnames[n]
if s == "" {
// Give each tmp a different name so that they can be registerized.
// Add a preceding . to avoid clashing with legal names.
prefix := ".autotmp_%d"
s = fmt.Sprintf(prefix, n)
autotmpnames[n] = s
}
return s
}
// f is method type, with receiver.
// return function type, receiver as first argument (or not).
func NewMethodType(sig *types.Type, recv *types.Type) *types.Type {
nrecvs := 0
if recv != nil {
nrecvs++
}
// TODO(mdempsky): Move this function to types.
// TODO(mdempsky): Preserve positions, names, and package from sig+recv.
params := make([]*types.Field, nrecvs+sig.Params().Fields().Len())
if recv != nil {
params[0] = types.NewField(base.Pos, nil, recv)
}
for i, param := range sig.Params().Fields().Slice() {
d := types.NewField(base.Pos, nil, param.Type)
d.SetIsDDD(param.IsDDD())
params[nrecvs+i] = d
}
results := make([]*types.Field, sig.Results().Fields().Len())
for i, t := range sig.Results().Fields().Slice() {
results[i] = types.NewField(base.Pos, nil, t.Type)
}
return types.NewSignature(nil, params, results)
}