mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Ran rsc.io/grind rev 6f0e601 on the source files. The cleanups move var declarations as close to the use as possible, splitting disjoint uses of the var into separate variables. They also remove dead code (especially in func sudoaddable), which helps with the var moving. There's more cleanup to come, but this alone cuts the time spent compiling html/template on my 2013 MacBook Pro from 3.1 seconds to 2.3 seconds. Change-Id: I4de499f47b1dd47a560c310bbcde6b08d425cfd6 Reviewed-on: https://go-review.googlesource.com/5637 Reviewed-by: Rob Pike <r@golang.org>
1485 lines
28 KiB
Go
1485 lines
28 KiB
Go
// 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 gc
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func dflag() bool {
|
|
if Debug['d'] == 0 {
|
|
return false
|
|
}
|
|
if Debug['y'] != 0 {
|
|
return true
|
|
}
|
|
if incannedimport != 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/*
|
|
* declaration stack & operations
|
|
*/
|
|
func dcopy(a *Sym, b *Sym) {
|
|
a.Pkg = b.Pkg
|
|
a.Name = b.Name
|
|
a.Def = b.Def
|
|
a.Block = b.Block
|
|
a.Lastlineno = b.Lastlineno
|
|
}
|
|
|
|
func push() *Sym {
|
|
d := new(Sym)
|
|
d.Lastlineno = lineno
|
|
d.Link = dclstack
|
|
dclstack = d
|
|
return d
|
|
}
|
|
|
|
func pushdcl(s *Sym) *Sym {
|
|
d := push()
|
|
dcopy(d, s)
|
|
if dflag() {
|
|
fmt.Printf("\t%v push %v %p\n", Ctxt.Line(int(lineno)), Sconv(s, 0), s.Def)
|
|
}
|
|
return d
|
|
}
|
|
|
|
func popdcl() {
|
|
var d *Sym
|
|
var s *Sym
|
|
var lno int
|
|
|
|
// if(dflag())
|
|
// print("revert\n");
|
|
|
|
for d = dclstack; d != nil; d = d.Link {
|
|
if d.Name == "" {
|
|
break
|
|
}
|
|
s = Pkglookup(d.Name, d.Pkg)
|
|
lno = int(s.Lastlineno)
|
|
dcopy(s, d)
|
|
d.Lastlineno = int32(lno)
|
|
if dflag() {
|
|
fmt.Printf("\t%v pop %v %p\n", Ctxt.Line(int(lineno)), Sconv(s, 0), s.Def)
|
|
}
|
|
}
|
|
|
|
if d == nil {
|
|
Fatal("popdcl: no mark")
|
|
}
|
|
dclstack = d.Link
|
|
block = d.Block
|
|
}
|
|
|
|
func poptodcl() {
|
|
// pop the old marker and push a new one
|
|
// (cannot reuse the existing one)
|
|
// because we use the markers to identify blocks
|
|
// for the goto restriction checks.
|
|
popdcl()
|
|
|
|
markdcl()
|
|
}
|
|
|
|
func markdcl() {
|
|
d := push()
|
|
d.Name = "" // used as a mark in fifo
|
|
d.Block = block
|
|
|
|
blockgen++
|
|
block = blockgen
|
|
}
|
|
|
|
// if(dflag())
|
|
// print("markdcl\n");
|
|
func dumpdcl(st string) {
|
|
var s *Sym
|
|
|
|
i := 0
|
|
for d := dclstack; d != nil; d = d.Link {
|
|
i++
|
|
fmt.Printf(" %.2d %p", i, d)
|
|
if d.Name == "" {
|
|
fmt.Printf("\n")
|
|
continue
|
|
}
|
|
|
|
fmt.Printf(" '%s'", d.Name)
|
|
s = Pkglookup(d.Name, d.Pkg)
|
|
fmt.Printf(" %v\n", Sconv(s, 0))
|
|
}
|
|
}
|
|
|
|
func testdclstack() {
|
|
for d := dclstack; d != nil; d = d.Link {
|
|
if d.Name == "" {
|
|
if nerrors != 0 {
|
|
errorexit()
|
|
}
|
|
Yyerror("mark left on the stack")
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func redeclare(s *Sym, where string) {
|
|
if s.Lastlineno == 0 {
|
|
var tmp *Strlit
|
|
if s.Origpkg != nil {
|
|
tmp = s.Origpkg.Path
|
|
} else {
|
|
tmp = s.Pkg.Path
|
|
}
|
|
pkgstr := tmp
|
|
Yyerror("%v redeclared %s\n"+"\tprevious declaration during import \"%v\"", Sconv(s, 0), where, Zconv(pkgstr, 0))
|
|
} else {
|
|
line1 := parserline()
|
|
line2 := int(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 {
|
|
line2 = line1
|
|
line1 = int(s.Lastlineno)
|
|
}
|
|
|
|
yyerrorl(int(line1), "%v redeclared %s\n"+"\tprevious declaration at %v", Sconv(s, 0), where, Ctxt.Line(line2))
|
|
}
|
|
}
|
|
|
|
var vargen int
|
|
|
|
/*
|
|
* declare individual names - var, typ, const
|
|
*/
|
|
|
|
var declare_typegen int
|
|
|
|
func declare(n *Node, ctxt int) {
|
|
if ctxt == PDISCARD {
|
|
return
|
|
}
|
|
|
|
if isblank(n) {
|
|
return
|
|
}
|
|
|
|
n.Lineno = int32(parserline())
|
|
s := n.Sym
|
|
|
|
// kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
|
|
if importpkg == nil && typecheckok == 0 && s.Pkg != localpkg {
|
|
Yyerror("cannot declare name %v", Sconv(s, 0))
|
|
}
|
|
|
|
if ctxt == PEXTERN && s.Name == "init" {
|
|
Yyerror("cannot declare init - must be func", s)
|
|
}
|
|
|
|
gen := 0
|
|
if ctxt == PEXTERN {
|
|
externdcl = list(externdcl, n)
|
|
if dflag() {
|
|
fmt.Printf("\t%v global decl %v %p\n", Ctxt.Line(int(lineno)), Sconv(s, 0), n)
|
|
}
|
|
} else {
|
|
if Curfn == nil && ctxt == PAUTO {
|
|
Fatal("automatic outside function")
|
|
}
|
|
if Curfn != nil {
|
|
Curfn.Dcl = list(Curfn.Dcl, n)
|
|
}
|
|
if n.Op == OTYPE {
|
|
declare_typegen++
|
|
gen = declare_typegen
|
|
} else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "·") {
|
|
vargen++
|
|
gen = vargen
|
|
}
|
|
pushdcl(s)
|
|
n.Curfn = Curfn
|
|
}
|
|
|
|
if ctxt == PAUTO {
|
|
n.Xoffset = 0
|
|
}
|
|
|
|
if s.Block == block {
|
|
// functype will print errors about duplicate function arguments.
|
|
// Don't repeat the error here.
|
|
if ctxt != PPARAM && ctxt != PPARAMOUT {
|
|
redeclare(s, "in this block")
|
|
}
|
|
}
|
|
|
|
s.Block = block
|
|
s.Lastlineno = int32(parserline())
|
|
s.Def = n
|
|
n.Vargen = int32(gen)
|
|
n.Funcdepth = Funcdepth
|
|
n.Class = uint8(ctxt)
|
|
|
|
autoexport(n, ctxt)
|
|
}
|
|
|
|
func addvar(n *Node, t *Type, ctxt int) {
|
|
if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil {
|
|
Fatal("addvar: n=%v t=%v nil", Nconv(n, 0), Tconv(t, 0))
|
|
}
|
|
|
|
n.Op = ONAME
|
|
declare(n, ctxt)
|
|
n.Type = t
|
|
}
|
|
|
|
/*
|
|
* declare variables from grammar
|
|
* new_name_list (type | [type] = expr_list)
|
|
*/
|
|
func variter(vl *NodeList, t *Node, el *NodeList) *NodeList {
|
|
init := (*NodeList)(nil)
|
|
doexpr := el != nil
|
|
|
|
if count(el) == 1 && count(vl) > 1 {
|
|
e := el.N
|
|
as2 := Nod(OAS2, nil, nil)
|
|
as2.List = vl
|
|
as2.Rlist = list1(e)
|
|
var v *Node
|
|
for ; vl != nil; vl = vl.Next {
|
|
v = vl.N
|
|
v.Op = ONAME
|
|
declare(v, dclcontext)
|
|
v.Ntype = t
|
|
v.Defn = as2
|
|
if Funcdepth > 0 {
|
|
init = list(init, Nod(ODCL, v, nil))
|
|
}
|
|
}
|
|
|
|
return list(init, as2)
|
|
}
|
|
|
|
var v *Node
|
|
var e *Node
|
|
for ; vl != nil; vl = vl.Next {
|
|
if doexpr {
|
|
if el == nil {
|
|
Yyerror("missing expression in var declaration")
|
|
break
|
|
}
|
|
|
|
e = el.N
|
|
el = el.Next
|
|
} else {
|
|
e = nil
|
|
}
|
|
|
|
v = vl.N
|
|
v.Op = ONAME
|
|
declare(v, dclcontext)
|
|
v.Ntype = t
|
|
|
|
if e != nil || Funcdepth > 0 || isblank(v) {
|
|
if Funcdepth > 0 {
|
|
init = list(init, Nod(ODCL, v, nil))
|
|
}
|
|
e = Nod(OAS, v, e)
|
|
init = list(init, e)
|
|
if e.Right != nil {
|
|
v.Defn = e
|
|
}
|
|
}
|
|
}
|
|
|
|
if el != nil {
|
|
Yyerror("extra expression in var declaration")
|
|
}
|
|
return init
|
|
}
|
|
|
|
/*
|
|
* declare constants from grammar
|
|
* new_name_list [[type] = expr_list]
|
|
*/
|
|
func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList {
|
|
vv := (*NodeList)(nil)
|
|
if cl == nil {
|
|
if t != nil {
|
|
Yyerror("const declaration cannot have type without expression")
|
|
}
|
|
cl = lastconst
|
|
t = lasttype
|
|
} else {
|
|
lastconst = cl
|
|
lasttype = t
|
|
}
|
|
|
|
cl = listtreecopy(cl)
|
|
|
|
var v *Node
|
|
var c *Node
|
|
for ; vl != nil; vl = vl.Next {
|
|
if cl == nil {
|
|
Yyerror("missing value in const declaration")
|
|
break
|
|
}
|
|
|
|
c = cl.N
|
|
cl = cl.Next
|
|
|
|
v = vl.N
|
|
v.Op = OLITERAL
|
|
declare(v, dclcontext)
|
|
|
|
v.Ntype = t
|
|
v.Defn = c
|
|
|
|
vv = list(vv, Nod(ODCLCONST, v, nil))
|
|
}
|
|
|
|
if cl != nil {
|
|
Yyerror("extra expression in const declaration")
|
|
}
|
|
iota_ += 1
|
|
return vv
|
|
}
|
|
|
|
/*
|
|
* this generates a new name node,
|
|
* typically for labels or other one-off names.
|
|
*/
|
|
func newname(s *Sym) *Node {
|
|
if s == nil {
|
|
Fatal("newname nil")
|
|
}
|
|
|
|
n := Nod(ONAME, nil, nil)
|
|
n.Sym = s
|
|
n.Type = nil
|
|
n.Addable = 1
|
|
n.Ullman = 1
|
|
n.Xoffset = 0
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* this generates a new name node for a name
|
|
* being declared.
|
|
*/
|
|
func dclname(s *Sym) *Node {
|
|
n := newname(s)
|
|
n.Op = ONONAME // caller will correct it
|
|
return n
|
|
}
|
|
|
|
func typenod(t *Type) *Node {
|
|
// if we copied another type with *t = *u
|
|
// then t->nod might be out of date, so
|
|
// check t->nod->type too
|
|
if t.Nod == nil || t.Nod.Type != t {
|
|
t.Nod = Nod(OTYPE, nil, nil)
|
|
t.Nod.Type = t
|
|
t.Nod.Sym = t.Sym
|
|
}
|
|
|
|
return t.Nod
|
|
}
|
|
|
|
/*
|
|
* this will return an old name
|
|
* that has already been pushed on the
|
|
* declaration list. a diagnostic is
|
|
* generated if no name has been defined.
|
|
*/
|
|
func oldname(s *Sym) *Node {
|
|
n := s.Def
|
|
if n == nil {
|
|
// maybe a top-level name will come along
|
|
// to give this a definition later.
|
|
// walkdef will check s->def again once
|
|
// all the input source has been processed.
|
|
n = newname(s)
|
|
|
|
n.Op = ONONAME
|
|
n.Iota = iota_ // save current iota value in const declarations
|
|
}
|
|
|
|
if Curfn != nil && n.Funcdepth > 0 && n.Funcdepth != Funcdepth && n.Op == ONAME {
|
|
// inner func is referring to var in outer func.
|
|
//
|
|
// TODO(rsc): If there is an outer variable x and we
|
|
// are parsing x := 5 inside the closure, until we get to
|
|
// the := it looks like a reference to the outer x so we'll
|
|
// make x a closure variable unnecessarily.
|
|
if n.Closure == nil || n.Closure.Funcdepth != Funcdepth {
|
|
// create new closure var.
|
|
c := Nod(ONAME, nil, nil)
|
|
|
|
c.Sym = s
|
|
c.Class = PPARAMREF
|
|
c.Isddd = n.Isddd
|
|
c.Defn = n
|
|
c.Addable = 0
|
|
c.Ullman = 2
|
|
c.Funcdepth = Funcdepth
|
|
c.Outer = n.Closure
|
|
n.Closure = c
|
|
c.Closure = n
|
|
c.Xoffset = 0
|
|
Curfn.Cvars = list(Curfn.Cvars, c)
|
|
}
|
|
|
|
// return ref to closure var, not original
|
|
return n.Closure
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* := declarations
|
|
*/
|
|
func colasname(n *Node) bool {
|
|
switch n.Op {
|
|
case ONAME,
|
|
ONONAME,
|
|
OPACK,
|
|
OTYPE,
|
|
OLITERAL:
|
|
return n.Sym != nil
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func colasdefn(left *NodeList, defn *Node) {
|
|
for l := left; l != nil; l = l.Next {
|
|
if l.N.Sym != nil {
|
|
l.N.Sym.Flags |= SymUniq
|
|
}
|
|
}
|
|
|
|
nnew := 0
|
|
nerr := 0
|
|
var n *Node
|
|
for l := left; l != nil; l = l.Next {
|
|
n = l.N
|
|
if isblank(n) {
|
|
continue
|
|
}
|
|
if !colasname(n) {
|
|
yyerrorl(int(defn.Lineno), "non-name %v on left side of :=", Nconv(n, 0))
|
|
nerr++
|
|
continue
|
|
}
|
|
|
|
if n.Sym.Flags&SymUniq == 0 {
|
|
yyerrorl(int(defn.Lineno), "%v repeated on left side of :=", Sconv(n.Sym, 0))
|
|
n.Diag++
|
|
nerr++
|
|
continue
|
|
}
|
|
|
|
n.Sym.Flags &^= SymUniq
|
|
if n.Sym.Block == block {
|
|
continue
|
|
}
|
|
|
|
nnew++
|
|
n = newname(n.Sym)
|
|
declare(n, dclcontext)
|
|
n.Defn = defn
|
|
defn.Ninit = list(defn.Ninit, Nod(ODCL, n, nil))
|
|
l.N = n
|
|
}
|
|
|
|
if nnew == 0 && nerr == 0 {
|
|
yyerrorl(int(defn.Lineno), "no new variables on left side of :=")
|
|
}
|
|
}
|
|
|
|
func colas(left *NodeList, right *NodeList, lno int32) *Node {
|
|
as := Nod(OAS2, nil, nil)
|
|
as.List = left
|
|
as.Rlist = right
|
|
as.Colas = 1
|
|
as.Lineno = lno
|
|
colasdefn(left, as)
|
|
|
|
// make the tree prettier; not necessary
|
|
if count(left) == 1 && count(right) == 1 {
|
|
as.Left = as.List.N
|
|
as.Right = as.Rlist.N
|
|
as.List = nil
|
|
as.Rlist = nil
|
|
as.Op = OAS
|
|
}
|
|
|
|
return as
|
|
}
|
|
|
|
/*
|
|
* declare the arguments in an
|
|
* interface field declaration.
|
|
*/
|
|
func ifacedcl(n *Node) {
|
|
if n.Op != ODCLFIELD || n.Right == nil {
|
|
Fatal("ifacedcl")
|
|
}
|
|
|
|
if isblank(n.Left) {
|
|
Yyerror("methods must have a unique non-blank name")
|
|
}
|
|
|
|
dclcontext = PPARAM
|
|
markdcl()
|
|
Funcdepth++
|
|
n.Outer = Curfn
|
|
Curfn = n
|
|
funcargs(n.Right)
|
|
|
|
// funcbody is normally called after the parser has
|
|
// seen the body of a function but since an interface
|
|
// field declaration does not have a body, we must
|
|
// call it now to pop the current declaration context.
|
|
dclcontext = PAUTO
|
|
|
|
funcbody(n)
|
|
}
|
|
|
|
/*
|
|
* declare the function proper
|
|
* and declare the arguments.
|
|
* called in extern-declaration context
|
|
* returns in auto-declaration context.
|
|
*/
|
|
func funchdr(n *Node) {
|
|
// change the declaration context from extern to auto
|
|
if Funcdepth == 0 && dclcontext != PEXTERN {
|
|
Fatal("funchdr: dclcontext")
|
|
}
|
|
|
|
dclcontext = PAUTO
|
|
markdcl()
|
|
Funcdepth++
|
|
|
|
n.Outer = Curfn
|
|
Curfn = n
|
|
|
|
if n.Nname != nil {
|
|
funcargs(n.Nname.Ntype)
|
|
} else if n.Ntype != nil {
|
|
funcargs(n.Ntype)
|
|
} else {
|
|
funcargs2(n.Type)
|
|
}
|
|
}
|
|
|
|
func funcargs(nt *Node) {
|
|
if nt.Op != OTFUNC {
|
|
Fatal("funcargs %v", Oconv(int(nt.Op), 0))
|
|
}
|
|
|
|
// re-start the variable generation number
|
|
// we want to use small numbers for the return variables,
|
|
// so let them have the chunk starting at 1.
|
|
vargen = count(nt.Rlist)
|
|
|
|
// declare the receiver and in arguments.
|
|
// no n->defn because type checking of func header
|
|
// will not fill in the types until later
|
|
if nt.Left != nil {
|
|
n := nt.Left
|
|
if n.Op != ODCLFIELD {
|
|
Fatal("funcargs receiver %v", Oconv(int(n.Op), 0))
|
|
}
|
|
if n.Left != nil {
|
|
n.Left.Op = ONAME
|
|
n.Left.Ntype = n.Right
|
|
declare(n.Left, PPARAM)
|
|
if dclcontext == PAUTO {
|
|
vargen++
|
|
n.Left.Vargen = int32(vargen)
|
|
}
|
|
}
|
|
}
|
|
|
|
var n *Node
|
|
for l := nt.List; l != nil; l = l.Next {
|
|
n = l.N
|
|
if n.Op != ODCLFIELD {
|
|
Fatal("funcargs in %v", Oconv(int(n.Op), 0))
|
|
}
|
|
if n.Left != nil {
|
|
n.Left.Op = ONAME
|
|
n.Left.Ntype = n.Right
|
|
declare(n.Left, PPARAM)
|
|
if dclcontext == PAUTO {
|
|
vargen++
|
|
n.Left.Vargen = int32(vargen)
|
|
}
|
|
}
|
|
}
|
|
|
|
// declare the out arguments.
|
|
gen := count(nt.List)
|
|
var i int = 0
|
|
var nn *Node
|
|
for l := nt.Rlist; l != nil; l = l.Next {
|
|
n = l.N
|
|
|
|
if n.Op != ODCLFIELD {
|
|
Fatal("funcargs out %v", Oconv(int(n.Op), 0))
|
|
}
|
|
|
|
if n.Left == nil {
|
|
// Name so that escape analysis can track it. ~r stands for 'result'.
|
|
namebuf = fmt.Sprintf("~r%d", gen)
|
|
gen++
|
|
|
|
n.Left = newname(Lookup(namebuf))
|
|
}
|
|
|
|
// TODO: n->left->missing = 1;
|
|
n.Left.Op = ONAME
|
|
|
|
if isblank(n.Left) {
|
|
// 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.
|
|
// We do not record a pointer to the original node (n->orig).
|
|
// Having multiple names causes too much confusion in later passes.
|
|
nn = Nod(OXXX, nil, nil)
|
|
|
|
*nn = *n.Left
|
|
nn.Orig = nn
|
|
namebuf = fmt.Sprintf("~b%d", gen)
|
|
gen++
|
|
nn.Sym = Lookup(namebuf)
|
|
n.Left = nn
|
|
}
|
|
|
|
n.Left.Ntype = n.Right
|
|
declare(n.Left, PPARAMOUT)
|
|
if dclcontext == PAUTO {
|
|
i++
|
|
n.Left.Vargen = int32(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 *Type) {
|
|
if t.Etype != TFUNC {
|
|
Fatal("funcargs2 %v", Tconv(t, 0))
|
|
}
|
|
|
|
if t.Thistuple != 0 {
|
|
var n *Node
|
|
for ft := getthisx(t).Type; ft != nil; ft = ft.Down {
|
|
if ft.Nname == nil || ft.Nname.Sym == nil {
|
|
continue
|
|
}
|
|
n = ft.Nname // no need for newname(ft->nname->sym)
|
|
n.Type = ft.Type
|
|
declare(n, PPARAM)
|
|
}
|
|
}
|
|
|
|
if t.Intuple != 0 {
|
|
var n *Node
|
|
for ft := getinargx(t).Type; ft != nil; ft = ft.Down {
|
|
if ft.Nname == nil || ft.Nname.Sym == nil {
|
|
continue
|
|
}
|
|
n = ft.Nname
|
|
n.Type = ft.Type
|
|
declare(n, PPARAM)
|
|
}
|
|
}
|
|
|
|
if t.Outtuple != 0 {
|
|
var n *Node
|
|
for ft := getoutargx(t).Type; ft != nil; ft = ft.Down {
|
|
if ft.Nname == nil || ft.Nname.Sym == nil {
|
|
continue
|
|
}
|
|
n = ft.Nname
|
|
n.Type = ft.Type
|
|
declare(n, PPARAMOUT)
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* finish the body.
|
|
* called in auto-declaration context.
|
|
* returns in extern-declaration context.
|
|
*/
|
|
func funcbody(n *Node) {
|
|
// change the declaration context from auto to extern
|
|
if dclcontext != PAUTO {
|
|
Fatal("funcbody: dclcontext")
|
|
}
|
|
popdcl()
|
|
Funcdepth--
|
|
Curfn = n.Outer
|
|
n.Outer = nil
|
|
if Funcdepth == 0 {
|
|
dclcontext = PEXTERN
|
|
}
|
|
}
|
|
|
|
/*
|
|
* new type being defined with name s.
|
|
*/
|
|
func typedcl0(s *Sym) *Node {
|
|
n := newname(s)
|
|
n.Op = OTYPE
|
|
declare(n, dclcontext)
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* node n, which was returned by typedcl0
|
|
* is being declared to have uncompiled type t.
|
|
* return the ODCLTYPE node to use.
|
|
*/
|
|
func typedcl1(n *Node, t *Node, local int) *Node {
|
|
n.Ntype = t
|
|
n.Local = uint8(local)
|
|
return Nod(ODCLTYPE, n, nil)
|
|
}
|
|
|
|
/*
|
|
* structs, functions, and methods.
|
|
* they don't belong here, but where do they belong?
|
|
*/
|
|
func checkembeddedtype(t *Type) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
if t.Sym == nil && Isptr[t.Etype] != 0 {
|
|
t = t.Type
|
|
if t.Etype == TINTER {
|
|
Yyerror("embedded type cannot be a pointer to interface")
|
|
}
|
|
}
|
|
|
|
if Isptr[t.Etype] != 0 {
|
|
Yyerror("embedded type cannot be a pointer")
|
|
} else if t.Etype == TFORW && t.Embedlineno == 0 {
|
|
t.Embedlineno = lineno
|
|
}
|
|
}
|
|
|
|
func structfield(n *Node) *Type {
|
|
lno := int(lineno)
|
|
lineno = n.Lineno
|
|
|
|
if n.Op != ODCLFIELD {
|
|
Fatal("structfield: oops %v\n", Nconv(n, 0))
|
|
}
|
|
|
|
f := typ(TFIELD)
|
|
f.Isddd = n.Isddd
|
|
|
|
if n.Right != nil {
|
|
typecheck(&n.Right, Etype)
|
|
n.Type = n.Right.Type
|
|
if n.Left != nil {
|
|
n.Left.Type = n.Type
|
|
}
|
|
if n.Embedded != 0 {
|
|
checkembeddedtype(n.Type)
|
|
}
|
|
}
|
|
|
|
n.Right = nil
|
|
|
|
f.Type = n.Type
|
|
if f.Type == nil {
|
|
f.Broke = 1
|
|
}
|
|
|
|
switch n.Val.Ctype {
|
|
case CTSTR:
|
|
f.Note = n.Val.U.Sval
|
|
|
|
default:
|
|
Yyerror("field annotation must be string")
|
|
fallthrough
|
|
|
|
// fallthrough
|
|
case CTxxx:
|
|
f.Note = nil
|
|
}
|
|
|
|
if n.Left != nil && n.Left.Op == ONAME {
|
|
f.Nname = n.Left
|
|
f.Embedded = n.Embedded
|
|
f.Sym = f.Nname.Sym
|
|
}
|
|
|
|
lineno = int32(lno)
|
|
return f
|
|
}
|
|
|
|
var uniqgen uint32
|
|
|
|
func checkdupfields(t *Type, what string) {
|
|
lno := int(lineno)
|
|
|
|
for ; t != nil; t = t.Down {
|
|
if t.Sym != nil && t.Nname != nil && !isblank(t.Nname) {
|
|
if t.Sym.Uniqgen == uniqgen {
|
|
lineno = t.Nname.Lineno
|
|
Yyerror("duplicate %s %s", what, t.Sym.Name)
|
|
} else {
|
|
t.Sym.Uniqgen = uniqgen
|
|
}
|
|
}
|
|
}
|
|
|
|
lineno = int32(lno)
|
|
}
|
|
|
|
/*
|
|
* convert a parsed id/type list into
|
|
* a type for struct/interface/arglist
|
|
*/
|
|
func tostruct(l *NodeList) *Type {
|
|
var f *Type
|
|
t := typ(TSTRUCT)
|
|
|
|
for tp := &t.Type; l != nil; l = l.Next {
|
|
f = structfield(l.N)
|
|
|
|
*tp = f
|
|
tp = &f.Down
|
|
}
|
|
|
|
for f := t.Type; f != nil && t.Broke == 0; f = f.Down {
|
|
if f.Broke != 0 {
|
|
t.Broke = 1
|
|
}
|
|
}
|
|
|
|
uniqgen++
|
|
checkdupfields(t.Type, "field")
|
|
|
|
if t.Broke == 0 {
|
|
checkwidth(t)
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
func tofunargs(l *NodeList) *Type {
|
|
var f *Type
|
|
|
|
t := typ(TSTRUCT)
|
|
t.Funarg = 1
|
|
|
|
for tp := &t.Type; l != nil; l = l.Next {
|
|
f = structfield(l.N)
|
|
f.Funarg = 1
|
|
|
|
// esc.c needs to find f given a PPARAM to add the tag.
|
|
if l.N.Left != nil && l.N.Left.Class == PPARAM {
|
|
l.N.Left.Paramfld = f
|
|
}
|
|
|
|
*tp = f
|
|
tp = &f.Down
|
|
}
|
|
|
|
for f := t.Type; f != nil && t.Broke == 0; f = f.Down {
|
|
if f.Broke != 0 {
|
|
t.Broke = 1
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
func interfacefield(n *Node) *Type {
|
|
lno := int(lineno)
|
|
lineno = n.Lineno
|
|
|
|
if n.Op != ODCLFIELD {
|
|
Fatal("interfacefield: oops %v\n", Nconv(n, 0))
|
|
}
|
|
|
|
if n.Val.Ctype != CTxxx {
|
|
Yyerror("interface method cannot have annotation")
|
|
}
|
|
|
|
f := typ(TFIELD)
|
|
f.Isddd = n.Isddd
|
|
|
|
if n.Right != nil {
|
|
if n.Left != nil {
|
|
// queue resolution of method type for later.
|
|
// right now all we need is the name list.
|
|
// avoids cycles for recursive interface types.
|
|
n.Type = typ(TINTERMETH)
|
|
|
|
n.Type.Nname = n.Right
|
|
n.Left.Type = n.Type
|
|
queuemethod(n)
|
|
|
|
if n.Left.Op == ONAME {
|
|
f.Nname = n.Left
|
|
f.Embedded = n.Embedded
|
|
f.Sym = f.Nname.Sym
|
|
}
|
|
} else {
|
|
typecheck(&n.Right, Etype)
|
|
n.Type = n.Right.Type
|
|
|
|
if n.Embedded != 0 {
|
|
checkembeddedtype(n.Type)
|
|
}
|
|
|
|
if n.Type != nil {
|
|
switch n.Type.Etype {
|
|
case TINTER:
|
|
break
|
|
|
|
case TFORW:
|
|
Yyerror("interface type loop involving %v", Tconv(n.Type, 0))
|
|
f.Broke = 1
|
|
|
|
default:
|
|
Yyerror("interface contains embedded non-interface %v", Tconv(n.Type, 0))
|
|
f.Broke = 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
n.Right = nil
|
|
|
|
f.Type = n.Type
|
|
if f.Type == nil {
|
|
f.Broke = 1
|
|
}
|
|
|
|
lineno = int32(lno)
|
|
return f
|
|
}
|
|
|
|
func tointerface(l *NodeList) *Type {
|
|
var f *Type
|
|
var t1 *Type
|
|
|
|
t := typ(TINTER)
|
|
|
|
tp := &t.Type
|
|
for ; l != nil; l = l.Next {
|
|
f = interfacefield(l.N)
|
|
|
|
if l.N.Left == nil && f.Type.Etype == TINTER {
|
|
// embedded interface, inline methods
|
|
for t1 = f.Type.Type; t1 != nil; t1 = t1.Down {
|
|
f = typ(TFIELD)
|
|
f.Type = t1.Type
|
|
f.Broke = t1.Broke
|
|
f.Sym = t1.Sym
|
|
if f.Sym != nil {
|
|
f.Nname = newname(f.Sym)
|
|
}
|
|
*tp = f
|
|
tp = &f.Down
|
|
}
|
|
} else {
|
|
*tp = f
|
|
tp = &f.Down
|
|
}
|
|
}
|
|
|
|
for f := t.Type; f != nil && t.Broke == 0; f = f.Down {
|
|
if f.Broke != 0 {
|
|
t.Broke = 1
|
|
}
|
|
}
|
|
|
|
uniqgen++
|
|
checkdupfields(t.Type, "method")
|
|
t = sortinter(t)
|
|
checkwidth(t)
|
|
|
|
return t
|
|
}
|
|
|
|
func embedded(s *Sym, pkg *Pkg) *Node {
|
|
const (
|
|
CenterDot = 0xB7
|
|
)
|
|
// Names sometimes have disambiguation junk
|
|
// appended after a center dot. Discard it when
|
|
// making the name for the embedded struct field.
|
|
name := s.Name
|
|
|
|
if i := strings.Index(s.Name, string(CenterDot)); i >= 0 {
|
|
name = s.Name[:i]
|
|
}
|
|
|
|
var n *Node
|
|
if exportname(name) {
|
|
n = newname(Lookup(name))
|
|
} else if s.Pkg == builtinpkg {
|
|
// The name of embedded builtins belongs to pkg.
|
|
n = newname(Pkglookup(name, pkg))
|
|
} else {
|
|
n = newname(Pkglookup(name, s.Pkg))
|
|
}
|
|
n = Nod(ODCLFIELD, n, oldname(s))
|
|
n.Embedded = 1
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* check that the list of declarations is either all anonymous or all named
|
|
*/
|
|
func findtype(l *NodeList) *Node {
|
|
for ; l != nil; l = l.Next {
|
|
if l.N.Op == OKEY {
|
|
return l.N.Right
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func checkarglist(all *NodeList, input int) *NodeList {
|
|
named := 0
|
|
for l := all; l != nil; l = l.Next {
|
|
if l.N.Op == OKEY {
|
|
named = 1
|
|
break
|
|
}
|
|
}
|
|
|
|
if named != 0 {
|
|
n := (*Node)(nil)
|
|
var l *NodeList
|
|
for l = all; l != nil; l = l.Next {
|
|
n = l.N
|
|
if n.Op != OKEY && n.Sym == nil {
|
|
Yyerror("mixed named and unnamed function parameters")
|
|
break
|
|
}
|
|
}
|
|
|
|
if l == nil && n != nil && n.Op != OKEY {
|
|
Yyerror("final function parameter must have type")
|
|
}
|
|
}
|
|
|
|
nextt := (*Node)(nil)
|
|
var t *Node
|
|
var n *Node
|
|
for l := all; l != nil; l = l.Next {
|
|
// can cache result from findtype to avoid
|
|
// quadratic behavior here, but unlikely to matter.
|
|
n = l.N
|
|
|
|
if named != 0 {
|
|
if n.Op == OKEY {
|
|
t = n.Right
|
|
n = n.Left
|
|
nextt = nil
|
|
} else {
|
|
if nextt == nil {
|
|
nextt = findtype(l)
|
|
}
|
|
t = nextt
|
|
}
|
|
} else {
|
|
t = n
|
|
n = nil
|
|
}
|
|
|
|
// during import l->n->op is OKEY, but l->n->left->sym == S
|
|
// means it was a '?', not that it was
|
|
// a lone type This doesn't matter for the exported
|
|
// declarations, which are parsed by rules that don't
|
|
// use checkargs, but can happen for func literals in
|
|
// the inline bodies.
|
|
// TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ?
|
|
if importpkg != nil && n.Sym == nil {
|
|
n = nil
|
|
}
|
|
|
|
if n != nil && n.Sym == nil {
|
|
t = n
|
|
n = nil
|
|
}
|
|
|
|
if n != nil {
|
|
n = newname(n.Sym)
|
|
}
|
|
n = Nod(ODCLFIELD, n, t)
|
|
if n.Right != nil && n.Right.Op == ODDD {
|
|
if input == 0 {
|
|
Yyerror("cannot use ... in output argument list")
|
|
} else if l.Next != nil {
|
|
Yyerror("can only use ... as final argument in list")
|
|
}
|
|
n.Right.Op = OTARRAY
|
|
n.Right.Right = n.Right.Left
|
|
n.Right.Left = nil
|
|
n.Isddd = 1
|
|
if n.Left != nil {
|
|
n.Left.Isddd = 1
|
|
}
|
|
}
|
|
|
|
l.N = n
|
|
}
|
|
|
|
return all
|
|
}
|
|
|
|
func fakethis() *Node {
|
|
n := Nod(ODCLFIELD, nil, typenod(Ptrto(typ(TSTRUCT))))
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* Is this field a method on an interface?
|
|
* Those methods have an anonymous
|
|
* *struct{} as the receiver.
|
|
* (See fakethis above.)
|
|
*/
|
|
func isifacemethod(f *Type) bool {
|
|
rcvr := getthisx(f).Type
|
|
if rcvr.Sym != nil {
|
|
return false
|
|
}
|
|
t := rcvr.Type
|
|
if Isptr[t.Etype] == 0 {
|
|
return false
|
|
}
|
|
t = t.Type
|
|
if t.Sym != nil || t.Etype != TSTRUCT || t.Type != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/*
|
|
* turn a parsed function declaration
|
|
* into a type
|
|
*/
|
|
func functype(this *Node, in *NodeList, out *NodeList) *Type {
|
|
t := typ(TFUNC)
|
|
|
|
rcvr := (*NodeList)(nil)
|
|
if this != nil {
|
|
rcvr = list1(this)
|
|
}
|
|
t.Type = tofunargs(rcvr)
|
|
t.Type.Down = tofunargs(out)
|
|
t.Type.Down.Down = tofunargs(in)
|
|
|
|
uniqgen++
|
|
checkdupfields(t.Type.Type, "argument")
|
|
checkdupfields(t.Type.Down.Type, "argument")
|
|
checkdupfields(t.Type.Down.Down.Type, "argument")
|
|
|
|
if t.Type.Broke != 0 || t.Type.Down.Broke != 0 || t.Type.Down.Down.Broke != 0 {
|
|
t.Broke = 1
|
|
}
|
|
|
|
if this != nil {
|
|
t.Thistuple = 1
|
|
}
|
|
t.Outtuple = count(out)
|
|
t.Intuple = count(in)
|
|
t.Outnamed = 0
|
|
if t.Outtuple > 0 && out.N.Left != nil && out.N.Left.Orig != nil {
|
|
s := out.N.Left.Orig.Sym
|
|
if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result
|
|
t.Outnamed = 1
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
var methodsym_toppkg *Pkg
|
|
|
|
func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
|
|
var s *Sym
|
|
var p string
|
|
var suffix string
|
|
var spkg *Pkg
|
|
|
|
t := t0
|
|
if t == nil {
|
|
goto bad
|
|
}
|
|
s = t.Sym
|
|
if s == nil && Isptr[t.Etype] != 0 {
|
|
t = t.Type
|
|
if t == nil {
|
|
goto bad
|
|
}
|
|
s = t.Sym
|
|
}
|
|
|
|
spkg = nil
|
|
if s != nil {
|
|
spkg = s.Pkg
|
|
}
|
|
|
|
// if t0 == *t and t0 has a sym,
|
|
// we want to see *t, not t0, in the method name.
|
|
if t != t0 && t0.Sym != nil {
|
|
t0 = Ptrto(t)
|
|
}
|
|
|
|
suffix = ""
|
|
if iface != 0 {
|
|
dowidth(t0)
|
|
if t0.Width < Types[Tptr].Width {
|
|
suffix = "·i"
|
|
}
|
|
}
|
|
|
|
if (spkg == nil || nsym.Pkg != spkg) && !exportname(nsym.Name) {
|
|
if t0.Sym == nil && Isptr[t0.Etype] != 0 {
|
|
p = fmt.Sprintf("(%v).%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix)
|
|
} else {
|
|
p = fmt.Sprintf("%v.%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix)
|
|
}
|
|
} else {
|
|
if t0.Sym == nil && Isptr[t0.Etype] != 0 {
|
|
p = fmt.Sprintf("(%v).%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix)
|
|
} else {
|
|
p = fmt.Sprintf("%v.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix)
|
|
}
|
|
}
|
|
|
|
if spkg == nil {
|
|
if methodsym_toppkg == nil {
|
|
methodsym_toppkg = mkpkg(newstrlit("go"))
|
|
}
|
|
spkg = methodsym_toppkg
|
|
}
|
|
|
|
s = Pkglookup(p, spkg)
|
|
|
|
return s
|
|
|
|
bad:
|
|
Yyerror("illegal receiver type: %v", Tconv(t0, 0))
|
|
return nil
|
|
}
|
|
|
|
func methodname(n *Node, t *Type) *Node {
|
|
s := methodsym(n.Sym, t, 0)
|
|
if s == nil {
|
|
return n
|
|
}
|
|
return newname(s)
|
|
}
|
|
|
|
func methodname1(n *Node, t *Node) *Node {
|
|
star := ""
|
|
if t.Op == OIND {
|
|
star = "*"
|
|
t = t.Left
|
|
}
|
|
|
|
if t.Sym == nil || isblank(n) {
|
|
return newname(n.Sym)
|
|
}
|
|
|
|
var p string
|
|
if star != "" {
|
|
p = fmt.Sprintf("(%s%v).%v", star, Sconv(t.Sym, 0), Sconv(n.Sym, 0))
|
|
} else {
|
|
p = fmt.Sprintf("%v.%v", Sconv(t.Sym, 0), Sconv(n.Sym, 0))
|
|
}
|
|
|
|
if exportname(t.Sym.Name) {
|
|
n = newname(Lookup(p))
|
|
} else {
|
|
n = newname(Pkglookup(p, t.Sym.Pkg))
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
/*
|
|
* add a method, declared as a function,
|
|
* n is fieldname, pa is base type, t is function type
|
|
*/
|
|
func addmethod(sf *Sym, t *Type, local bool, nointerface bool) {
|
|
// get field sym
|
|
if sf == nil {
|
|
Fatal("no method symbol")
|
|
}
|
|
|
|
// get parent type sym
|
|
pa := getthisx(t).Type // ptr to this structure
|
|
if pa == nil {
|
|
Yyerror("missing receiver")
|
|
return
|
|
}
|
|
|
|
pa = pa.Type
|
|
f := methtype(pa, 1)
|
|
if f == nil {
|
|
t = pa
|
|
if t == nil { // rely on typecheck having complained before
|
|
return
|
|
}
|
|
if t != nil {
|
|
if Isptr[t.Etype] != 0 {
|
|
if t.Sym != nil {
|
|
Yyerror("invalid receiver type %v (%v is a pointer type)", Tconv(pa, 0), Tconv(t, 0))
|
|
return
|
|
}
|
|
|
|
t = t.Type
|
|
}
|
|
|
|
if t.Broke != 0 { // rely on typecheck having complained before
|
|
return
|
|
}
|
|
if t.Sym == nil {
|
|
Yyerror("invalid receiver type %v (%v is an unnamed type)", Tconv(pa, 0), Tconv(t, 0))
|
|
return
|
|
}
|
|
|
|
if Isptr[t.Etype] != 0 {
|
|
Yyerror("invalid receiver type %v (%v is a pointer type)", Tconv(pa, 0), Tconv(t, 0))
|
|
return
|
|
}
|
|
|
|
if t.Etype == TINTER {
|
|
Yyerror("invalid receiver type %v (%v is an interface type)", Tconv(pa, 0), Tconv(t, 0))
|
|
return
|
|
}
|
|
}
|
|
|
|
// Should have picked off all the reasons above,
|
|
// but just in case, fall back to generic error.
|
|
Yyerror("invalid receiver type %v (%v / %v)", Tconv(pa, 0), Tconv(pa, obj.FmtLong), Tconv(t, obj.FmtLong))
|
|
|
|
return
|
|
}
|
|
|
|
pa = f
|
|
if pa.Etype == TSTRUCT {
|
|
for f := pa.Type; f != nil; f = f.Down {
|
|
if f.Sym == sf {
|
|
Yyerror("type %v has both field and method named %v", Tconv(pa, 0), Sconv(sf, 0))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if local && pa.Local == 0 {
|
|
// defining method on non-local type.
|
|
Yyerror("cannot define new methods on non-local type %v", Tconv(pa, 0))
|
|
|
|
return
|
|
}
|
|
|
|
n := Nod(ODCLFIELD, newname(sf), nil)
|
|
n.Type = t
|
|
|
|
d := (*Type)(nil) // last found
|
|
for f := pa.Method; f != nil; f = f.Down {
|
|
d = f
|
|
if f.Etype != TFIELD {
|
|
Fatal("addmethod: not TFIELD: %v", Tconv(f, obj.FmtLong))
|
|
}
|
|
if sf.Name != f.Sym.Name {
|
|
continue
|
|
}
|
|
if !Eqtype(t, f.Type) {
|
|
Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", Tconv(pa, 0), Sconv(sf, 0), Tconv(f.Type, 0), Tconv(t, 0))
|
|
}
|
|
return
|
|
}
|
|
|
|
f = structfield(n)
|
|
f.Nointerface = nointerface
|
|
|
|
// during import unexported method names should be in the type's package
|
|
if importpkg != nil && f.Sym != nil && !exportname(f.Sym.Name) && f.Sym.Pkg != structpkg {
|
|
Fatal("imported method name %v in wrong package %s\n", Sconv(f.Sym, obj.FmtSign), structpkg.Name)
|
|
}
|
|
|
|
if d == nil {
|
|
pa.Method = f
|
|
} else {
|
|
d.Down = f
|
|
}
|
|
return
|
|
}
|
|
|
|
func funccompile(n *Node) {
|
|
Stksize = BADWIDTH
|
|
Maxarg = 0
|
|
|
|
if n.Type == nil {
|
|
if nerrors == 0 {
|
|
Fatal("funccompile missing type")
|
|
}
|
|
return
|
|
}
|
|
|
|
// assign parameter offsets
|
|
checkwidth(n.Type)
|
|
|
|
if Curfn != nil {
|
|
Fatal("funccompile %v inside %v", Sconv(n.Nname.Sym, 0), Sconv(Curfn.Nname.Sym, 0))
|
|
}
|
|
|
|
Stksize = 0
|
|
dclcontext = PAUTO
|
|
Funcdepth = n.Funcdepth + 1
|
|
compile(n)
|
|
Curfn = nil
|
|
Funcdepth = 0
|
|
dclcontext = PEXTERN
|
|
}
|
|
|
|
func funcsym(s *Sym) *Sym {
|
|
p := fmt.Sprintf("%s·f", s.Name)
|
|
s1 := Pkglookup(p, s.Pkg)
|
|
|
|
if s1.Def == nil {
|
|
s1.Def = newname(s1)
|
|
s1.Def.Shortname = newname(s)
|
|
funcsyms = list(funcsyms, s1.Def)
|
|
}
|
|
|
|
return s1
|
|
}
|