Allows removing a bunch of unnecessary fields.
Passes toolstash/buildall.
Change-Id: Iec2492920e1c3ef352a9bf4296c74a55d9cc9ad6
Reviewed-on: https://go-review.googlesource.com/20677
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
// size is the length in bytes of the memory included in the run.
// next is the index just after the end of the memory run.
// TODO(mdempsky): Eliminate fields parameter once struct fields are kept in slices.
-func memrun(t *Type, fields []*Type, start int) (size int64, next int) {
+func memrun(t *Type, fields []*Field, start int) (size int64, next int) {
next = start
for {
next++
// ispaddedfield reports whether the i'th field of struct type t is followed
// by padding. The caller is responsible for providing t.FieldSlice() as fields.
// TODO(mdempsky): Eliminate fields parameter once struct fields are kept in slices.
-func ispaddedfield(t *Type, fields []*Type, i int) bool {
+func ispaddedfield(t *Type, fields []*Field, i int) bool {
if t.Etype != TSTRUCT {
Fatalf("ispaddedfield called non-struct %v", t)
}
func offmod(t *Type) {
o := int32(0)
for f, it := IterFields(t); f != nil; f = it.Next() {
- if f.Etype != TFIELD {
- Fatalf("offmod: not TFIELD: %v", Tconv(f, obj.FmtLong))
- }
f.Width = int64(o)
o += int32(Widthptr)
if int64(o) >= Thearch.MAXWIDTH {
lastzero := int64(0)
var w int64
for f, it := IterFields(t); f != nil; f = it.Next() {
- if f.Etype != TFIELD {
- Fatalf("widstruct: not TFIELD: %v", Tconv(f, obj.FmtLong))
- }
if f.Type == nil {
// broken field, just skip it so that other valid fields
// get a width.
// pick off named types
if sym := t.Sym; sym != nil {
- // Fields should be exported by p.field().
- if t.Etype == TFIELD {
- Fatalf("exporter: printing a field/parameter with wrong function")
- }
// Predeclared types should have been found in the type map.
if t.Orig == t {
Fatalf("exporter: predeclared type missing from type map?")
// sort methods for reproducible export format
// TODO(gri) Determine if they are already sorted
// in which case we can drop this step.
- var methods []*Type
+ var methods []*Field
for m, it := IterMethods(t); m != nil; m = it.Next() {
methods = append(methods, m)
}
}
}
-func (p *exporter) field(f *Type) {
- if f.Etype != TFIELD {
- Fatalf("exporter: field expected")
- }
-
+func (p *exporter) field(f *Field) {
p.fieldName(f)
p.typ(f.Type)
p.note(f.Note)
}
}
-func (p *exporter) method(m *Type) {
- if m.Etype != TFIELD {
- Fatalf("exporter: method expected")
- }
-
+func (p *exporter) method(m *Field) {
p.fieldName(m)
// TODO(gri) For functions signatures, we use p.typ() to export
// so we could share the same type with multiple functions. Do
// fieldName is like qualifiedName but it doesn't record the package
// for blank (_) or exported names.
-func (p *exporter) fieldName(t *Type) {
+func (p *exporter) fieldName(t *Field) {
sym := t.Sym
var name string
if t.Embedded == 0 {
name = sym.Name
- } else if bname := basetypeName(t); bname != "" && !exportname(bname) {
+ } else if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
// anonymous field with unexported base type name: use "?" as field name
// (bname != "" per spec, but we are conservative in case of errors)
name = "?"
}
}
-func (p *exporter) param(q *Type, n int) {
- if q.Etype != TFIELD {
- Fatalf("exporter: parameter expected")
- }
+func (p *exporter) param(q *Field, n int) {
t := q.Type
if q.Isddd {
// create a fake type to encode ... just for the p.typ call
p.note(q.Note)
}
-func parName(q *Type) string {
+func parName(q *Field) string {
if q.Sym == nil {
return ""
}
t = t.Type
}
- t = t.Results().Field(0)
- if t != nil {
- return t.Width + Ctxt.FixedFrameSize()
+ f := t.Results().Field(0)
+ if f != nil {
+ return f.Width + Ctxt.FixedFrameSize()
}
}
f := xfunc.Func.Nname
// We are going to insert captured variables before input args.
- var params []*Type
+ var params []*Field
var decls []*Node
for _, v := range func_.Func.Cvars.Slice() {
if v.Op == OXXX {
continue
}
- fld := typ(TFIELD)
+ fld := newField()
fld.Funarg = true
if v.Name.Byval {
// If v is captured by value, we merely downgrade it to PPARAM.
}
}
-func structfield(n *Node) *Type {
+func structfield(n *Node) *Field {
lno := lineno
lineno = n.Lineno
Fatalf("structfield: oops %v\n", n)
}
- f := typ(TFIELD)
+ f := newField()
f.Isddd = n.Isddd
if n.Right != nil {
Fatalf("struct expected")
}
- var fields []*Type
+ var fields []*Field
for _, n := range l {
fields = append(fields, structfield(n))
}
t := typ(TSTRUCT)
t.Funarg = true
- var fields []*Type
+ var fields []*Field
for _, n := range l {
f := structfield(n)
f.Funarg = true
return t
}
-func interfacefield(n *Node) *Type {
+func interfacefield(n *Node) *Field {
lno := lineno
lineno = n.Lineno
Yyerror("interface method cannot have annotation")
}
- f := typ(TFIELD)
+ f := newField()
f.Isddd = n.Isddd
if n.Right != nil {
Fatalf("interface expected")
}
- var fields []*Type
+ var fields []*Field
for _, n := range l {
f := interfacefield(n)
if n.Left == nil && f.Type.Etype == TINTER {
// embedded interface, inline methods
for t1, it := IterFields(f.Type); t1 != nil; t1 = it.Next() {
- f = typ(TFIELD)
+ f = newField()
f.Type = t1.Type
f.Broke = t1.Broke
f.Sym = t1.Sym
}
// get parent type sym
- pa := t.Recv() // ptr to this structure
- if pa == nil {
+ rf := t.Recv() // ptr to this structure
+ if rf == nil {
Yyerror("missing receiver")
return
}
- pa = pa.Type // base type
- f := methtype(pa, 1)
- if f == nil {
+ pa := rf.Type // base type
+ mt := methtype(pa, 1)
+ if mt == nil {
t = pa
if t == nil { // rely on typecheck having complained before
return
return
}
- pa = f
+ pa = mt
if local && !pa.Local {
Yyerror("cannot define new methods on non-local type %v", pa)
return
n := Nod(ODCLFIELD, newname(msym), nil)
n.Type = t
- var d *Type // last found
+ var d *Field // last found
for f, it := IterMethods(pa); f != nil; f = it.Next() {
d = f
- if f.Etype != TFIELD {
- Fatalf("addmethod: not TFIELD: %v", Tconv(f, obj.FmtLong))
- }
if msym.Name != f.Sym.Name {
continue
}
return
}
- f = structfield(n)
+ f := structfield(n)
f.Nointerface = nointerface
// during import unexported method names should be in the type's package
}
// methodbyname sorts types by symbol name.
-type methodbyname []*Type
+type methodbyname []*Field
func (x methodbyname) Len() int { return len(x) }
func (x methodbyname) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
if t == nil {
return
}
- if t.Etype == TFIELD {
- Fatalf("unexpected TFIELD in dumpexporttype")
- }
if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
return
}
return
}
- var m []*Type
+ var m []*Field
for f, it := IterMethods(t); f != nil; f = it.Next() {
dumpexporttype(f.Type)
m = append(m, f)
if Debug['l'] < 2 {
typecheckinl(f.Type.Nname)
}
- exportf("\tfunc (%v) %v %v { %v }\n", Tconv(f.Type.Recv(), obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp))
+ exportf("\tfunc %v %v %v { %v }\n", Tconv(f.Type.Recvs(), obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp))
reexportdeplist(f.Type.Nname.Func.Inl)
} else {
- exportf("\tfunc (%v) %v %v\n", Tconv(f.Type.Recv(), obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
+ exportf("\tfunc %v %v %v\n", Tconv(f.Type.Recvs(), obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
}
}
}
TMAP: "MAP",
TINTER: "INTER",
TFORW: "FORW",
- TFIELD: "FIELD",
TSTRING: "STRING",
TUNSAFEPTR: "TUNSAFEPTR",
TANY: "ANY",
}
// Unless the 'l' flag was specified, if the type has a name, just print that name.
- if flag&obj.FmtLong == 0 && t.Sym != nil && t.Etype != TFIELD && t != Types[t.Etype] {
+ if flag&obj.FmtLong == 0 && t.Sym != nil && t != Types[t.Etype] {
switch fmtmode {
case FTypeId:
if flag&obj.FmtShort != 0 {
buf.WriteString("(")
if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
- buf.WriteString(Tconv(t1, obj.FmtShort))
+ buf.WriteString(Fldconv(t1, obj.FmtShort))
if t1.Down != nil {
buf.WriteString(", ")
}
}
} else {
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
- buf.WriteString(Tconv(t1, 0))
+ buf.WriteString(Fldconv(t1, 0))
if t1.Down != nil {
buf.WriteString(", ")
}
buf.WriteString("struct {")
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
buf.WriteString(" ")
- buf.WriteString(Tconv(t1, obj.FmtLong))
+ buf.WriteString(Fldconv(t1, obj.FmtLong))
if t1.Down != nil {
buf.WriteString(";")
}
}
return buf.String()
- case TFIELD:
- var name string
- if flag&obj.FmtShort == 0 {
- s := t.Sym
-
- // Take the name from the original, lest we substituted it with ~r%d or ~b%d.
- // ~r%d is a (formerly) unnamed result.
- if (fmtmode == FErr || fmtmode == FExp) && t.Nname != nil {
- if t.Nname.Orig != nil {
- s = t.Nname.Orig.Sym
- if s != nil && s.Name[0] == '~' {
- if s.Name[1] == 'r' { // originally an unnamed result
- s = nil
- } else if s.Name[1] == 'b' { // originally the blank identifier _
- s = Lookup("_")
- }
- }
- } else {
- s = nil
- }
- }
-
- if s != nil && t.Embedded == 0 {
- if t.Funarg {
- name = Nconv(t.Nname, 0)
- } else if flag&obj.FmtLong != 0 {
- name = Sconv(s, obj.FmtShort|obj.FmtByte) // qualify non-exported names (used on structs, not on funarg)
- } else {
- name = Sconv(s, 0)
- }
- } else if fmtmode == FExp {
- // TODO(rsc) this breaks on the eliding of unused arguments in the backend
- // when this is fixed, the special case in dcl.go checkarglist can go.
- //if(t->funarg)
- // fmtstrcpy(fp, "_ ");
- //else
- if t.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 {
- name = fmt.Sprintf("@%q.?", s.Pkg.Path)
- } else {
- name = "?"
- }
- }
- }
-
- var typ string
- if t.Isddd {
- typ = "..." + Tconv(t.Type.Type, 0)
- } else {
- typ = Tconv(t.Type, 0)
- }
-
- str := typ
- if name != "" {
- str = name + " " + typ
- }
-
- // The fmtbody flag is intended to suppress escape analysis annotations
- // when printing a function type used in a function body.
- // (The escape analysis tags do not apply to func vars.)
- // But it must not suppress struct field tags.
- // See golang.org/issue/13777 and golang.org/issue/14331.
- if flag&obj.FmtShort == 0 && (!fmtbody || !t.Funarg) && t.Note != nil {
- str += " " + strconv.Quote(*t.Note)
- }
- return str
-
case TFORW:
if t.Sym != nil {
return fmt.Sprintf("undefined %v", t.Sym)
return Tconv(t, 0)
}
+func Fldconv(f *Field, flag int) string {
+ if f == nil {
+ return "<T>"
+ }
+
+ sf := flag
+ sm, sb := setfmode(&flag)
+
+ if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) {
+ fmtpkgpfx++
+ }
+ if fmtpkgpfx != 0 {
+ flag |= obj.FmtUnsigned
+ }
+
+ var name string
+ if flag&obj.FmtShort == 0 {
+ s := f.Sym
+
+ // Take the name from the original, lest we substituted it with ~r%d or ~b%d.
+ // ~r%d is a (formerly) unnamed result.
+ if (fmtmode == FErr || fmtmode == FExp) && f.Nname != nil {
+ if f.Nname.Orig != nil {
+ s = f.Nname.Orig.Sym
+ if s != nil && s.Name[0] == '~' {
+ if s.Name[1] == 'r' { // originally an unnamed result
+ s = nil
+ } else if s.Name[1] == 'b' { // originally the blank identifier _
+ s = Lookup("_")
+ }
+ }
+ } else {
+ s = nil
+ }
+ }
+
+ if s != nil && f.Embedded == 0 {
+ if f.Funarg {
+ name = Nconv(f.Nname, 0)
+ } else if flag&obj.FmtLong != 0 {
+ name = Sconv(s, obj.FmtShort|obj.FmtByte) // qualify non-exported names (used on structs, not on funarg)
+ } else {
+ name = Sconv(s, 0)
+ }
+ } else if fmtmode == FExp {
+ // TODO(rsc) this breaks on the eliding of unused arguments in the backend
+ // when this is fixed, the special case in dcl.go checkarglist can go.
+ //if(t->funarg)
+ // fmtstrcpy(fp, "_ ");
+ //else
+ if f.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 {
+ name = fmt.Sprintf("@%q.?", s.Pkg.Path)
+ } else {
+ name = "?"
+ }
+ }
+ }
+
+ var typ string
+ if f.Isddd {
+ typ = "..." + Tconv(f.Type.Type, 0)
+ } else {
+ typ = Tconv(f.Type, 0)
+ }
+
+ str := typ
+ if name != "" {
+ str = name + " " + typ
+ }
+
+ // The fmtbody flag is intended to suppress escape analysis annotations
+ // when printing a function type used in a function body.
+ // (The escape analysis tags do not apply to func vars.)
+ // But it must not suppress struct field tags.
+ // See golang.org/issue/13777 and golang.org/issue/14331.
+ if flag&obj.FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != nil {
+ str += " " + strconv.Quote(*f.Note)
+ }
+
+ if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) {
+ fmtpkgpfx--
+ }
+
+ flag = sf
+ fmtbody = sb
+ fmtmode = sm
+ return str
+}
+
// Fmt "%T": types.
// Flags: 'l' print definition, not name
// 'h' omit 'func' and receiver from function types, short type names
}
for field, it := IterFields(t); field != nil; field = it.Next() {
- if field.Etype != TFIELD {
- Fatalf("bad struct")
- }
if !visitComponents(field.Type, startOffset+field.Width, f) {
return false
}
// incorrectly) passed a 1; the behavior is exactly
// the same as it is for 1, except that PARAMOUT is
// generated instead of PARAM.
-func nodarg(t *Type, fp int) *Node {
+func nodarg(t interface{}, fp int) *Node {
var n *Node
- // entire argument struct, not just one arg
- if t.Etype == TSTRUCT && t.Funarg {
+ switch t := t.(type) {
+ case *Type:
+ // entire argument struct, not just one arg
+ if t.Etype != TSTRUCT || !t.Funarg {
+ Fatalf("nodarg: bad type %v", t)
+ }
n = Nod(ONAME, nil, nil)
n.Sym = Lookup(".args")
n.Type = t
}
n.Xoffset = first.Width
n.Addable = true
- goto fp
- }
-
- if t.Etype != TFIELD {
- Fatalf("nodarg: not field %v", t)
- }
-
- if fp == 1 || fp == -1 {
- for _, n := range Curfn.Func.Dcl {
- if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
- return n
+ case *Field:
+ if fp == 1 || fp == -1 {
+ for _, n := range Curfn.Func.Dcl {
+ if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
+ return n
+ }
}
}
- }
-
- n = Nod(ONAME, nil, nil)
- n.Type = t.Type
- n.Sym = t.Sym
- if t.Width == BADWIDTH {
- Fatalf("nodarg: offset not computed for %v", t)
+ n = Nod(ONAME, nil, nil)
+ n.Type = t.Type
+ n.Sym = t.Sym
+ if t.Width == BADWIDTH {
+ Fatalf("nodarg: offset not computed for %v", t)
+ }
+ n.Xoffset = t.Width
+ n.Addable = true
+ n.Orig = t.Nname
+ default:
+ panic("unreachable")
}
- n.Xoffset = t.Width
- n.Addable = true
- n.Orig = t.Nname
// Rewrite argument named _ to __,
// or else the assignment to _ will be
// discarded during code generation.
-fp:
if isblank(n) {
n.Sym = Lookup("__")
}
safemode = save_safemode
}
-func tinlvar(t *Type) *Node {
+func tinlvar(t *Field) *Node {
if t.Nname != nil && !isblank(t.Nname) {
if t.Nname.Name.Inlvar == nil {
Fatalf("missing inlvar for %v\n", t.Nname)
}
// Synthesize a variable to store the inlined function's results in.
-func retvar(t *Type, i int) *Node {
+func retvar(t *Field, i int) *Node {
n := newname(Lookupf("~r%d", i))
n.Type = t.Type
n.Class = PAUTO
return 2*Widthptr + 2*Widthint
}
-func makefield(name string, t *Type) *Type {
- f := typ(TFIELD)
+func makefield(name string, t *Type) *Field {
+ f := newField()
f.Type = t
f.Sym = nopkg.Lookup(name)
return f
arr.Type = Types[TUINT8]
arr.Bound = BUCKETSIZE
- field := make([]*Type, 0, 5)
+ field := make([]*Field, 0, 5)
field = append(field, makefield("topbits", arr))
arr = typ(TARRAY)
arr.Type = keytype
}
bucket := mapbucket(t)
- var field [8]*Type
+ var field [8]*Field
field[0] = makefield("count", Types[TINT])
field[1] = makefield("flags", Types[TUINT8])
field[2] = makefield("B", Types[TUINT8])
// checkBucket uintptr
// }
// must match ../../../../runtime/hashmap.go:hiter.
- var field [12]*Type
+ var field [12]*Field
field[0] = makefield("key", Ptrto(t.Key()))
field[1] = makefield("val", Ptrto(t.Type))
field[2] = makefield("t", Ptrto(Types[TUINT8]))
// generating code if necessary.
var ms []*Sig
for f, it2 := IterAllMethods(mt); f != nil; f = it2.Next() {
- if f.Etype != TFIELD {
- Fatalf("methods: not field %v", f)
- }
if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 {
Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f)
}
func imethods(t *Type) []*Sig {
var methods []*Sig
for f, it := IterFields(t); f != nil; f = it.Next() {
- if f.Etype != TFIELD {
- Fatalf("imethods: not field")
- }
if f.Type.Etype != TFUNC || f.Sym == nil {
continue
}
fallthrough
default:
ret = true
-
- case TFIELD:
- Fatalf("haspointers: unexpected type, %v", t)
}
t.Haspointers = 1 + uint8(obj.Bool2int(ret))
case TSTRUCT:
// Find the last field that has pointers.
- var lastPtrField *Type
+ var lastPtrField *Field
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
if haspointers(t1.Type) {
lastPtrField = t1
// tracksym returns the symbol for tracking use of field/method f, assumed
// to be a member of struct/interface type t.
-func tracksym(t, f *Type) *Sym {
+func tracksym(t *Type, f *Field) *Sym {
return Pkglookup(Tconv(t, obj.FmtLeft)+"."+f.Sym.Name, trackpkg)
}
syma := Lookup("a")
symb := Lookup("b")
- var fields [2]*Type
- fields[0] = typ(TFIELD)
+ var fields [2]*Field
+ fields[0] = newField()
fields[0].Type = tk
fields[0].Sym = syma
- fields[1] = typ(TFIELD)
+ fields[1] = newField()
fields[1].Type = tv
fields[1].Sym = symb
{Name{}, 52, 80},
{Node{}, 92, 144},
{Sym{}, 60, 112},
- {Type{}, 140, 232},
+ {Type{}, 132, 224},
}
for _, tt := range tests {
var i int64
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
- if t1.Etype != TFIELD {
- panic("non-TFIELD in TSTRUCT")
- }
if t1.Sym != f.Sym {
i++
continue
}
// methcmp sorts by symbol, then by package path for unexported symbols.
-type methcmp []*Type
+type methcmp []*Field
func (x methcmp) Len() int { return len(x) }
func (x methcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
return false
}
- t1, i1 := IterFields(t1)
- t2, i2 := IterFields(t2)
+ f1, i1 := IterFields(t1)
+ f2, i2 := IterFields(t2)
for {
- if !Eqtype(t1, t2) {
+ if !Eqtype(f1.Type, f2.Type) {
return false
}
- if t1 == nil {
+ if f1 == nil {
return true
}
- t1 = i1.Next()
- t2 = i2.Next()
+ f1 = i1.Next()
+ f2 = i2.Next()
}
}
// 3. dst is an interface type and src implements dst.
if dst.Etype == TINTER && src.Etype != TNIL {
- var missing *Type
+ var missing, have *Field
var ptr int
- var have *Type
if implements(src, dst, &missing, &have, &ptr) {
return OCONVIFACE
}
}
if src.Etype == TINTER && dst.Etype != TBLANK {
- var have *Type
+ var missing, have *Field
var ptr int
- var missing *Type
if why != nil && implements(dst, src, &missing, &have, &ptr) {
*why = ": need type assertion"
}
t = (*types)[0]
*types = (*types)[1:]
- case TPTR32, TPTR64, TCHAN, TARRAY, TFIELD:
+ case TPTR32, TPTR64, TCHAN, TARRAY:
elem := substAny(t.Type, types)
if elem != t.Type {
t = t.Copy()
case TSTRUCT:
// nfs only has to be big enough for the builtin functions.
- var nfs [8]*Type
+ var nfs [8]*Field
fields := t.FieldSlice()
changed := false
for i, f := range fields {
- nf := substAny(f, types)
- if nf != f {
- if !changed {
- for j := 0; j < i; j++ {
- nfs[j] = fields[j].Copy()
- }
- }
+ nft := substAny(f.Type, types)
+ if nft != f.Type {
+ nfs[i] = f.Copy()
+ nfs[i].Type = nft
changed = true
- } else if changed {
- nf = f.Copy()
}
- nfs[i] = nf
}
+
if changed {
+ // Above we've initialized nfs with copied fields
+ // whenever the field type changed. However, because
+ // we keep fields in a linked list, we can only safely
+ // share the unmodified tail of the list. We need to
+ // copy the rest.
+ tail := true
+ for i := len(fields) - 1; i >= 0; i-- {
+ if nfs[i] != nil {
+ tail = false
+ } else if tail {
+ nfs[i] = fields[i]
+ } else {
+ nfs[i] = fields[i].Copy()
+ }
+ }
+
t = t.Copy()
t.SetFields(nfs[:len(fields)])
}
// A Dlist stores a pointer to a TFIELD Type embedded within
// a TSTRUCT or TINTER Type.
type Dlist struct {
- field *Type
+ field *Field
}
// dotlist is used by adddot1 to record the path of embedded fields
// lookdot0 returns the number of fields or methods named s associated
// with Type t. If exactly one exists, it will be returned in *save
// (if save is not nil).
-func lookdot0(s *Sym, t *Type, save **Type, ignorecase bool) int {
+func lookdot0(s *Sym, t *Type, save **Field, ignorecase bool) int {
u := t
if Isptr[u.Etype] {
u = u.Type
// in reverse order. If none exist, more will indicate whether t contains any
// embedded fields at depth d, so callers can decide whether to retry at
// a greater depth.
-func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase bool) (c int, more bool) {
+func adddot1(s *Sym, t *Type, d int, save **Field, ignorecase bool) (c int, more bool) {
if t.Trecur != 0 {
return
}
// a selection expression x.f, where x is of type t and f is the symbol s.
// If no such path exists, dotpath returns nil.
// If there are multiple shortest paths to the same depth, ambig is true.
-func dotpath(s *Sym, t *Type, save **Type, ignorecase bool) (path []Dlist, ambig bool) {
+func dotpath(s *Sym, t *Type, save **Field, ignorecase bool) (path []Dlist, ambig bool) {
// The embedding of types within structs imposes a tree structure onto
// types: structs parent the types they embed, and types parent their
// fields or methods. Our goal here is to find the shortest path to
// with unique tasks and they return
// the actual methods.
type Symlink struct {
- field *Type
+ field *Field
link *Symlink
good bool
followptr bool
// mark top-level method symbols
// so that expand1 doesn't consider them.
- var f *Type
for f, it := IterMethods(t); f != nil; f = it.Next() {
f.Sym.Flags |= SymUniq
}
// check each method to be uniquely reachable
for sl := slist; sl != nil; sl = sl.link {
sl.field.Sym.Flags &^= SymUniq
+ var f *Field
if path, _ := dotpath(sl.field.Sym, t, &f, false); path == nil {
continue
}
var genwrapper_linehistdone int = 0
-func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
+func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
if false && Debug['r'] != 0 {
fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam)
}
return n
}
-func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase bool) *Type {
+func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase bool) *Field {
*followptr = false
if t == nil {
return nil
}
- var m *Type
+ var m *Field
path, ambig := dotpath(s, t, &m, ignorecase)
if path == nil {
if ambig {
return m
}
-func implements(t *Type, iface *Type, m **Type, samename **Type, ptr *int) bool {
+func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
t0 := t
if t == nil {
return false
if t != nil {
expandmeth(t)
}
- var tm *Type
- var imtype *Type
- var followptr bool
- var rcvr *Type
for im, it := IterFields(iface); im != nil; im = it.Next() {
if im.Broke {
continue
}
- imtype = methodfunc(im.Type, nil)
- tm = ifacelookdot(im.Sym, t, &followptr, false)
+ imtype := methodfunc(im.Type, nil)
+ var followptr bool
+ tm := ifacelookdot(im.Sym, t, &followptr, false)
if tm == nil || tm.Nointerface || !Eqtype(methodfunc(tm.Type, nil), imtype) {
if tm == nil {
tm = ifacelookdot(im.Sym, t, &followptr, true)
// if pointer receiver in method,
// the method does not exist for value types.
- rcvr = tm.Type.Recv().Type
+ rcvr := tm.Type.Recv().Type
if Isptr[rcvr.Etype] && !Isptr[t0.Etype] && !followptr && !isifacemethod(tm.Type) {
if false && Debug['r'] != 0 {
// type switch
case Etype:
- var missing, have *Type
+ var missing, have *Field
var ptr int
switch {
case n1.Op == OLITERAL && Istype(n1.Type, TNIL):
Stackparam *Node // OPARAM node referring to stack copy of param
// ONAME PPARAM
- Field *Type // TFIELD in arg struct
+ Field *Field // TFIELD in arg struct
// ONAME closure param with PPARAMREF
Outer *Node // outer PPARAMREF in nested closure
TMAP
TINTER
TFORW
- TFIELD
TANY
TSTRING
TUNSAFEPTR
// A Type represents a Go type.
type Type struct {
Etype EType
- Nointerface bool
Noalg bool
Chan uint8
Trecur uint8 // to detect loops
Printed bool
- Embedded uint8 // TFIELD embedded type
- Funarg bool // on TSTRUCT and TFIELD
- Local bool // created in this file
+ Funarg bool // on TSTRUCT and TFIELD
+ Local bool // created in this file
Deferwidth bool
Broke bool // broken type definition.
- Isddd bool // TFIELD is ... argument
Align uint8
Haspointers uint8 // 0 unknown, 1 no, 2 yes
Intuple int
Outnamed bool
- Method *Type
- Xmethod *Type
+ Method *Field
+ Xmethod *Field
Sym *Sym
Vargen int32 // unique name for OTYPE/ONAME
Argwid int64
// most nodes
- Type *Type // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx
- Width int64 // offset in TFIELD, width in all others
+ Type *Type // element type for TARRAY, TCHAN, TMAP, TPTRxx
+ Width int64
// TSTRUCT
- Fields *Type // first struct field
+ Fields *Field // first struct field
- // TFIELD
- Down *Type // next struct field, also key type in TMAP
- Note *string // literal string annotation
+ Down *Type // key type in TMAP; next struct in Funarg TSTRUCT
// TARRAY
Bound int64 // negative is slice
Copyto []*Node
}
+// A Field represents a field in a struct or a method in an interface or
+// associated with a named type.
+type Field struct {
+ Nointerface bool
+ Embedded uint8 // embedded field
+ Funarg bool
+ Broke bool // broken field definition
+ Isddd bool // field is ... argument
+
+ Sym *Sym
+ Nname *Node
+
+ Type *Type // field type
+ Width int64 // TODO(mdempsky): Rename to offset.
+ Note *string // literal string annotation
+ Down *Field // next struct field
+}
+
// typ returns a new Type of the specified kind.
func typ(et EType) *Type {
t := &Type{
return t
}
+func newField() *Field {
+ return &Field{
+ Width: BADWIDTH,
+ }
+}
+
// Copy returns a shallow copy of the Type.
func (t *Type) Copy() *Type {
if t == nil {
return &nt
}
+func (f *Field) Copy() *Field {
+ nf := *f
+ return &nf
+}
+
// Iter provides an abstraction for iterating across struct fields and
// interface methods.
type Iter struct {
- x *Type
+ x *Field
}
// IterFields returns the first field or method in struct or interface type t
// and an Iter value to continue iterating across the rest.
-func IterFields(t *Type) (*Type, Iter) {
+func IterFields(t *Type) (*Field, Iter) {
if t.Etype != TSTRUCT && t.Etype != TINTER {
Fatalf("IterFields: type %v does not have fields", t)
}
// IterMethods returns the first method in type t's method set
// and an Iter value to continue iterating across the rest.
// IterMethods does not include promoted methods.
-func IterMethods(t *Type) (*Type, Iter) {
+func IterMethods(t *Type) (*Field, Iter) {
// TODO(mdempsky): Validate t?
return RawIter(t.Method)
}
// IterAllMethods returns the first (possibly promoted) method in type t's
// method set and an Iter value to continue iterating across the rest.
-func IterAllMethods(t *Type) (*Type, Iter) {
+func IterAllMethods(t *Type) (*Field, Iter) {
// TODO(mdempsky): Validate t?
return RawIter(t.Xmethod)
}
// RawIter returns field t and an Iter value to continue iterating across
// its successor fields. Most code should instead use one of the IterXXX
// functions above.
-func RawIter(t *Type) (*Type, Iter) {
+func RawIter(t *Field) (*Field, Iter) {
i := Iter{x: t}
f := i.Next()
return f, i
}
// Next returns the next field or method, if any.
-func (i *Iter) Next() *Type {
+func (i *Iter) Next() *Field {
if i.x == nil {
return nil
}
- t := i.x
- if t.Etype != TFIELD {
- Fatalf("Iter.Next: type %v is not a field", t)
- }
- i.x = t.Down
- return t
+ f := i.x
+ i.x = f.Down
+ return f
}
func (t *Type) wantEtype(et EType) {
func (t *Type) Params() *Type { return *t.ParamsP() }
func (t *Type) Results() *Type { return *t.ResultsP() }
-func (t *Type) Recv() *Type { return t.Recvs().Field(0) }
+func (t *Type) Recv() *Field { return t.Recvs().Field(0) }
// recvsParamsResults stores the accessor functions for a function Type's
// receiver, parameters, and result parameters, in that order.
}
// Field returns the i'th field/method of struct/interface type t.
-func (t *Type) Field(i int) *Type {
+func (t *Type) Field(i int) *Field {
// TODO: store fields in a slice so we can
// look them up by index in constant time.
for f, it := IterFields(t); f != nil; f = it.Next() {
// FieldSlice returns a slice of containing all fields/methods of
// struct/interface type t.
-func (t *Type) FieldSlice() []*Type {
- var s []*Type
+func (t *Type) FieldSlice() []*Field {
+ var s []*Field
for f, it := IterFields(t); f != nil; f = it.Next() {
s = append(s, f)
}
}
// SetFields sets struct/interface type t's fields/methods to fields.
-func (t *Type) SetFields(fields []*Type) {
+func (t *Type) SetFields(fields []*Field) {
if t.Etype != TSTRUCT && t.Etype != TINTER {
Fatalf("SetFields: type %v does not have fields", t)
}
- var next *Type
+ var next *Field
for i := len(fields) - 1; i >= 0; i-- {
fields[i].Down = next
next = fields[i]
}
switch t.Etype {
- case TMAP, TFIELD:
- // No special cases for these two, they are handled
- // by the general code after the switch.
+ case TMAP:
+ if c := t.Down.cmp(x.Down); c != ssa.CMPeq {
+ return c
+ }
case TPTR32, TPTR64:
- return t.Type.cmp(x.Type)
+ // No special cases for these two, they are handled
+ // by the general code after the switch.
case TSTRUCT:
if t.Map == nil {
panic(e)
}
- if c := t.Down.cmp(x.Down); c != ssa.CMPeq {
- return c
- }
+ // Common element type comparison for TARRAY, TCHAN, TMAP, TPTR32, and TPTR64.
return t.Type.cmp(x.Type)
}
}
if n.Type != nil && n.Type.Etype != TINTER {
- var have *Type
- var missing *Type
+ var missing, have *Field
var ptr int
if !implements(n.Type, t, &missing, &have, &ptr) {
if have != nil && have.Sym == missing.Sym {
return true
}
-func lookdot1(errnode *Node, s *Sym, t *Type, f *Type, dostrcmp int) *Type {
- var r *Type
+func lookdot1(errnode *Node, s *Sym, t *Type, f *Field, dostrcmp int) *Field {
+ var r *Field
for f, it := RawIter(f); f != nil; f = it.Next() {
if dostrcmp != 0 && f.Sym.Name == s.Name {
return f
// Find the base type: methtype will fail if t
// is not of the form T or *T.
- f2 := methtype(t, 0)
-
- if f2 == nil {
+ mt := methtype(t, 0)
+ if mt == nil {
return false
}
- expandmeth(f2)
- f2 = lookdot1(n, s, f2, f2.Xmethod, dostrcmp)
+ expandmeth(mt)
+ f2 := lookdot1(n, s, mt, mt.Xmethod, dostrcmp)
if f2 == nil {
return false
}
// dotField maps (*Type, *Sym) pairs to the corresponding struct field (*Type with Etype==TFIELD).
// It is a cache for use during usefield in walk.go, only enabled when field tracking.
-var dotField = map[typeSym]*Type{}
+var dotField = map[typeSym]*Field{}
-func lookdot(n *Node, t *Type, dostrcmp int) *Type {
+func lookdot(n *Node, t *Type, dostrcmp int) *Field {
s := n.Right.Sym
dowidth(t)
- var f1 *Type
+ var f1 *Field
if t.Etype == TSTRUCT || t.Etype == TINTER {
f1 = lookdot1(n, s, t, t.Fields, dostrcmp)
}
- var f2 *Type
+ var f2 *Field
if n.Left.Type == t || n.Left.Type.Sym == nil {
- f2 = methtype(t, 0)
- if f2 != nil {
+ mt := methtype(t, 0)
+ if mt != nil {
// Use f2->method, not f2->xmethod: adddot has
// already inserted all the necessary embedded dots.
- f2 = lookdot1(n, s, f2, f2.Method, dostrcmp)
+ f2 = lookdot1(n, s, mt, mt.Method, dostrcmp)
}
}
rcvr := typ(TSTRUCT)
rcvr.Funarg = true
- field := typ(TFIELD)
+ field := newField()
field.Type = Ptrto(typ(TSTRUCT))
- rcvr.SetFields([]*Type{field})
+ rcvr.SetFields([]*Field{field})
in := typ(TSTRUCT)
in.Funarg = true
out := typ(TSTRUCT)
out.Funarg = true
- field = typ(TFIELD)
+ field = newField()
field.Type = Types[TSTRING]
- out.SetFields([]*Type{field})
+ out.SetFields([]*Field{field})
f := typ(TFUNC)
*f.RecvsP() = rcvr
f.Outtuple = 1
t := typ(TINTER)
- field = typ(TFIELD)
+ field = newField()
field.Sym = Lookup("Error")
field.Type = f
- t.SetFields([]*Type{field})
+ t.SetFields([]*Field{field})
// error type
s := Pkglookup("error", builtinpkg)
}
// package all the arguments that match a ... T parameter into a []T.
-func mkdotargslice(lr0, nn []*Node, l *Type, fp int, init *Nodes, ddd *Node) []*Node {
+func mkdotargslice(lr0, nn []*Node, l *Field, fp int, init *Nodes, ddd *Node) []*Node {
esc := uint16(EscUnknown)
if ddd != nil {
esc = ddd.Esc
if s != "" {
s += ", "
}
- s += Tconv(l, 0)
+ s += Fldconv(l, 0)
}
if s == "" {
s = fmt.Sprintf("[no arguments %s]", what)
}
p0 := t.Params().Field(0)
res0 := t.Results().Field(0)
- var res1 *Type
+ var res1 *Field
if countfield(t.Results()) == 2 {
res1 = t.Results().Field(1)
}
return
}
}
- if Tconv(res0, 0) != "reflect.Method" {
+ if Tconv(res0.Type, 0) != "reflect.Method" {
return
}