blob: 472d8d2b8ad832ca08aa97e7a035efbd3bdcefae [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"
"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, tfn ir.Ntype) *ir.Func {
if tfn.Op() != ir.OTFUNC {
base.Fatalf("expected OTFUNC node, got %v", tfn)
}
fn := ir.NewFunc(base.Pos)
fn.Nname = ir.NewNameAt(base.Pos, sym)
fn.Nname.Func = fn
fn.Nname.Defn = fn
fn.Nname.Ntype = tfn
ir.MarkFunc(fn.Nname)
StartFuncBody(fn)
fn.Nname.Ntype = typecheckNtype(fn.Nname.Ntype)
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(), "cannot declare name %v", s)
}
if ctxt == ir.PEXTERN {
if s.Name == "init" {
base.ErrorfAt(n.Pos(), "cannot declare init - must be func")
}
if s.Name == "main" && s.Pkg.Name == "main" {
base.ErrorfAt(n.Pos(), "cannot declare main - must be func")
}
Target.Externs = append(Target.Externs, 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)
}
types.Pushdcl(s)
n.Curfn = ir.CurFunc
}
if ctxt == ir.PAUTO {
n.SetFrameOffset(0)
}
if s.Block == types.Block {
// functype will print errors about duplicate function arguments.
// Don't repeat the error here.
if ctxt != ir.PPARAM && ctxt != ir.PPARAMOUT {
Redeclared(n.Pos(), s, "in this block")
}
}
s.Block = types.Block
s.Lastlineno = base.Pos
s.Def = n
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)
}
// Redeclared emits a diagnostic about symbol s being redeclared at pos.
func Redeclared(pos src.XPos, s *types.Sym, where string) {
if !s.Lastlineno.IsKnown() {
var pkgName *ir.PkgName
if s.Def == nil {
for id, pkg := range DotImportRefs {
if id.Sym().Name == s.Name {
pkgName = pkg
break
}
}
} else {
pkgName = DotImportRefs[s.Def.(*ir.Ident)]
}
base.ErrorfAt(pos, "%v redeclared %s\n"+
"\t%v: previous declaration during import %q", s, where, base.FmtPos(pkgName.Pos()), pkgName.Pkg.Path)
} else {
prevPos := s.Lastlineno
// When an import and a declaration collide in separate files,
// present the import as the "redeclared", because the declaration
// is visible where the import is, but not vice versa.
// See issue 4510.
if s.Def == nil {
pos, prevPos = prevPos, pos
}
base.ErrorfAt(pos, "%v redeclared %s\n"+
"\t%v: previous declaration", s, where, base.FmtPos(prevPos))
}
}
// 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
types.Markdcl()
if fn.Nname.Ntype != nil {
funcargs(fn.Nname.Ntype.(*ir.FuncType))
} else {
funcargs2(fn.Type())
}
}
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
func FinishFuncBody() {
// change the declaration context from auto to previous context
types.Popdcl()
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))
}
}
// Add a method, declared as a function.
// - msym is the method symbol
// - t is function type (with receiver)
// Returns a pointer to the existing or added Field; or nil if there's an error.
func addmethod(n *ir.Func, msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field {
if msym == nil {
base.Fatalf("no method symbol")
}
// get parent type sym
rf := t.Recv() // ptr to this structure
if rf == nil {
base.Errorf("missing receiver")
return nil
}
mt := types.ReceiverBaseType(rf.Type)
if mt == nil || mt.Sym() == nil {
pa := rf.Type
t := pa
if t != nil && t.IsPtr() {
if t.Sym() != nil {
base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
return nil
}
t = t.Elem()
}
switch {
case t == nil || t.Broke():
// rely on typecheck having complained before
case t.Sym() == nil:
base.Errorf("invalid receiver type %v (%v is not a defined type)", pa, t)
case t.IsPtr():
base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
case t.IsInterface():
base.Errorf("invalid receiver type %v (%v is an interface type)", pa, t)
default:
// Should have picked off all the reasons above,
// but just in case, fall back to generic error.
base.Errorf("invalid receiver type %v (%L / %L)", pa, pa, t)
}
return nil
}
if local && mt.Sym().Pkg != types.LocalPkg {
base.Errorf("cannot define new methods on non-local type %v", mt)
return nil
}
if msym.IsBlank() {
return nil
}
if mt.IsStruct() {
for _, f := range mt.Fields().Slice() {
if f.Sym == msym {
base.Errorf("type %v has both field and method named %v", mt, msym)
f.SetBroke(true)
return nil
}
}
}
for _, f := range mt.Methods().Slice() {
if msym.Name != f.Sym.Name {
continue
}
// types.Identical only checks that incoming and result parameters match,
// so explicitly check that the receiver parameters match too.
if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
base.Errorf("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
}
return f
}
f := types.NewField(base.Pos, msym, t)
f.Nname = n.Nname
f.SetNointerface(nointerface)
mt.Methods().Append(f)
return f
}
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, "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
}
}
// TODO(mdempsky): Move to package types.
func FakeRecv() *types.Field {
return types.NewField(src.NoXPos, nil, types.FakeRecvType())
}
var fakeRecvField = FakeRecv
var funcStack []funcStackEnt // stack of previous values of ir.CurFunc/DeclContext
type funcStackEnt struct {
curfn *ir.Func
dclcontext ir.Class
}
func funcarg(n *ir.Field, ctxt ir.Class) {
if n.Sym == nil {
return
}
name := ir.NewNameAt(n.Pos, n.Sym)
n.Decl = name
name.Ntype = n.Ntype
Declare(name, ctxt)
}
func funcarg2(f *types.Field, ctxt ir.Class) {
if f.Sym == nil {
return
}
n := ir.NewNameAt(f.Pos, f.Sym)
f.Nname = n
n.SetType(f.Type)
Declare(n, ctxt)
}
func funcargs(nt *ir.FuncType) {
if nt.Op() != ir.OTFUNC {
base.Fatalf("funcargs %v", nt.Op())
}
// declare the receiver and in arguments.
if nt.Recv != nil {
funcarg(nt.Recv, ir.PPARAM)
}
for _, n := range nt.Params {
funcarg(n, ir.PPARAM)
}
// declare the out arguments.
for i, n := range nt.Results {
if n.Sym == nil {
// Name so that escape analysis can track it. ~r stands for 'result'.
n.Sym = LookupNum("~r", i)
}
if n.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.
n.Sym = LookupNum("~b", i)
}
funcarg(n, ir.PPARAMOUT)
}
}
// Same as funcargs, except run over an already constructed TFUNC.
// This happens during import, where the hidden_fndcl rule has
// used functype directly to parse the function's type.
func funcargs2(t *types.Type) {
if t.Kind() != types.TFUNC {
base.Fatalf("funcargs2 %v", t)
}
for _, f := range t.Recvs().Fields().Slice() {
funcarg2(f, ir.PPARAM)
}
for _, f := range t.Params().Fields().Slice() {
funcarg2(f, ir.PPARAM)
}
for _, f := range t.Results().Fields().Slice() {
funcarg2(f, ir.PPARAMOUT)
}
}
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"
// In quirks mode, pad out the number to stabilize variable
// sorting. This ensures autotmps 8 and 9 sort the same way even
// if they get renumbered to 9 and 10, respectively.
if base.Debug.UnifiedQuirks != 0 {
prefix = ".autotmp_%06d"
}
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 {
if sig.HasTParam() {
base.Fatalf("NewMethodType with type parameters in signature %+v", sig)
}
if recv != nil && recv.HasTParam() {
base.Fatalf("NewMethodType with type parameters in receiver %+v", recv)
}
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(types.LocalPkg, nil, nil, params, results)
}