diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 973016f97c..6b65dd0a52 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -1269,9 +1269,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) { n := Nod(ODCLFIELD, newname(msym), nil) n.Type = t - var d *Field // last found for f, it := IterMethods(pa); f != nil; f = it.Next() { - d = f if msym.Name != f.Sym.Name { continue } @@ -1291,11 +1289,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) { Fatalf("imported method name %v in wrong package %s\n", Sconv(f.Sym, FmtSign), tpkg.Name) } - if d == nil { - pa.Method = f - } else { - d.Down = f - } + pa.Methods().Append(f) } func funccompile(n *Node) { diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 55625e40e6..3af269d4dd 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -602,24 +602,24 @@ func typefmt(t *Type, flag FmtFlag) string { case TINTER: var buf bytes.Buffer buf.WriteString("interface {") - for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { + for i, f := range t.Fields().Slice() { + if i != 0 { + buf.WriteString(";") + } buf.WriteString(" ") switch { - case t1.Sym == nil: + case f.Sym == nil: // Check first that a symbol is defined for this type. // Wrong interface definitions may have types lacking a symbol. break - case exportname(t1.Sym.Name): - buf.WriteString(Sconv(t1.Sym, FmtShort)) + case exportname(f.Sym.Name): + buf.WriteString(Sconv(f.Sym, FmtShort)) default: - buf.WriteString(Sconv(t1.Sym, FmtUnsigned)) - } - buf.WriteString(Tconv(t1.Type, FmtShort)) - if t1.Down != nil { - buf.WriteString(";") + buf.WriteString(Sconv(f.Sym, FmtUnsigned)) } + buf.WriteString(Tconv(f.Type, FmtShort)) } - if t.Fields != nil { + if t.NumFields() != 0 { buf.WriteString(" ") } buf.WriteString("}") @@ -679,32 +679,27 @@ func typefmt(t *Type, flag FmtFlag) string { var buf bytes.Buffer if t.Funarg { buf.WriteString("(") + var flag1 FmtFlag 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(Fldconv(t1, FmtShort)) - if t1.Down != nil { - buf.WriteString(", ") - } - } - } else { - for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { - buf.WriteString(Fldconv(t1, 0)) - if t1.Down != nil { - buf.WriteString(", ") - } + flag1 = FmtShort + } + for i, f := range t.Fields().Slice() { + if i != 0 { + buf.WriteString(", ") } + buf.WriteString(Fldconv(f, flag1)) } buf.WriteString(")") } else { buf.WriteString("struct {") - for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { - buf.WriteString(" ") - buf.WriteString(Fldconv(t1, FmtLong)) - if t1.Down != nil { + for i, f := range t.Fields().Slice() { + if i != 0 { buf.WriteString(";") } + buf.WriteString(" ") + buf.WriteString(Fldconv(f, FmtLong)) } - if t.Fields != nil { + if t.NumFields() != 0 { buf.WriteString(" ") } buf.WriteString("}") diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index a8d0c93cf5..874d9e0069 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -285,7 +285,7 @@ func methods(t *Type) []*Sig { // make list of methods for t, // generating code if necessary. var ms []*Sig - for f, it2 := IterAllMethods(mt); f != nil; f = it2.Next() { + for _, f := range mt.AllMethods().Slice() { if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 { Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f) } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 31efa6269e..da4d036f71 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1725,12 +1725,10 @@ func adddot(n *Node) *Node { // the actual methods. type Symlink struct { field *Field - link *Symlink - good bool followptr bool } -var slist *Symlink +var slist []Symlink func expand0(t *Type, followptr bool) { u := t @@ -1740,17 +1738,12 @@ func expand0(t *Type, followptr bool) { } if u.Etype == TINTER { - var sl *Symlink for f, it := IterFields(u); f != nil; f = it.Next() { if f.Sym.Flags&SymUniq != 0 { continue } f.Sym.Flags |= SymUniq - sl = new(Symlink) - sl.field = f - sl.link = slist - sl.followptr = followptr - slist = sl + slist = append(slist, Symlink{field: f, followptr: followptr}) } return @@ -1758,17 +1751,12 @@ func expand0(t *Type, followptr bool) { u = methtype(t, 0) if u != nil { - var sl *Symlink for f, it := IterMethods(u); f != nil; f = it.Next() { if f.Sym.Flags&SymUniq != 0 { continue } f.Sym.Flags |= SymUniq - sl = new(Symlink) - sl.field = f - sl.link = slist - sl.followptr = followptr - slist = sl + slist = append(slist, Symlink{field: f, followptr: followptr}) } } } @@ -1808,7 +1796,7 @@ out: } func expandmeth(t *Type) { - if t == nil || t.Xmethod != nil { + if t == nil || t.AllMethods().Len() != 0 { return } @@ -1819,41 +1807,40 @@ func expandmeth(t *Type) { } // generate all reachable methods - slist = nil - + slist = slist[:0] expand1(t, true, false) // check each method to be uniquely reachable - for sl := slist; sl != nil; sl = sl.link { + var ms []*Field + for i, sl := range slist { + slist[i].field = nil sl.field.Sym.Flags &^= SymUniq + var f *Field if path, _ := dotpath(sl.field.Sym, t, &f, false); path == nil { continue } + // dotpath may have dug out arbitrary fields, we only want methods. - if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 { - sl.good = true - sl.field = f + if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 { + continue } + + // add it to the base type method list + f = f.Copy() + f.Embedded = 1 // needs a trampoline + if sl.followptr { + f.Embedded = 2 + } + ms = append(ms, f) } for f, it := IterMethods(t); f != nil; f = it.Next() { f.Sym.Flags &^= SymUniq } - t.Xmethod = t.Method - for sl := slist; sl != nil; sl = sl.link { - if sl.good { - // add it to the base type method list - f := sl.field.Copy() - f.Embedded = 1 // needs a trampoline - if sl.followptr { - f.Embedded = 2 - } - f.Down = t.Xmethod - t.Xmethod = f - } - } + ms = append(ms, t.Methods().Slice()...) + t.AllMethods().Set(ms) } // Given funarg struct list, return list of ODCLFIELD Node fn args. diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index 9d0207d476..9e285c058d 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -123,8 +123,8 @@ type Type struct { Intuple int Outnamed bool - Method *Field - Xmethod *Field + methods Fields + allMethods Fields Sym *Sym Vargen int32 // unique name for OTYPE/ONAME @@ -137,7 +137,7 @@ type Type struct { Width int64 // TSTRUCT - Fields *Field // first struct field + fields Fields Down *Type // key type in TMAP; next struct in Funarg TSTRUCT @@ -172,7 +172,48 @@ type Field struct { Type *Type // field type Width int64 // TODO(mdempsky): Rename to offset. Note *string // literal string annotation - Down *Field // next struct field +} + +// Fields is a pointer to a slice of *Field. +// This saves space in Types that do not have fields or methods +// compared to a simple slice of *Field. +type Fields struct { + s *[]*Field +} + +// Len returns the number of entries in f. +func (f *Fields) Len() int { + if f.s == nil { + return 0 + } + return len(*f.s) +} + +// Slice returns the entries in f as a slice. +// Changes to the slice entries will be reflected in f. +func (f *Fields) Slice() []*Field { + if f.s == nil { + return nil + } + return *f.s +} + +// Set sets f to a slice. +// This takes ownership of the slice. +func (f *Fields) Set(s []*Field) { + if len(s) != 0 { + f.s = &s + } else { + f.s = nil + } +} + +// Append appends entries to f. +func (f *Fields) Append(s ...*Field) { + if f.s == nil { + f.s = new([]*Field) + } + *f.s = append(*f.s, s...) } // typ returns a new Type of the specified kind. @@ -213,49 +254,38 @@ func (f *Field) Copy() *Field { // Iter provides an abstraction for iterating across struct fields and // interface methods. type Iter struct { - x *Field + s []*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) (*Field, Iter) { - if t.Etype != TSTRUCT && t.Etype != TINTER { - Fatalf("IterFields: type %v does not have fields", t) - } - return RawIter(t.Fields) + return t.Fields().Iter() } // 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) (*Field, Iter) { - // TODO(mdempsky): Validate t? - return RawIter(t.Method) + return t.Methods().Iter() } -// 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) (*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 *Field) (*Field, Iter) { - i := Iter{x: t} +// Iter returns the first field in fs and an Iter value to continue iterating +// across its successor fields. +// Deprecated: New code should use Slice instead. +func (fs *Fields) Iter() (*Field, Iter) { + i := Iter{s: fs.Slice()} f := i.Next() return f, i } // Next returns the next field or method, if any. func (i *Iter) Next() *Field { - if i.x == nil { + if len(i.s) == 0 { return nil } - f := i.x - i.x = f.Down + f := i.s[0] + i.s = i.s[1:] return f } @@ -311,40 +341,37 @@ func (t *Type) Key() *Type { return t.Down } +func (t *Type) Methods() *Fields { + // TODO(mdempsky): Validate t? + return &t.methods +} + +func (t *Type) AllMethods() *Fields { + // TODO(mdempsky): Validate t? + return &t.allMethods +} + +func (t *Type) Fields() *Fields { + if t.Etype != TSTRUCT && t.Etype != TINTER { + Fatalf("Fields: type %v does not have fields", t) + } + return &t.fields +} + // Field returns the i'th field/method of struct/interface type t. 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() { - if i == 0 { - return f - } - i-- - } - panic("not enough fields") + return t.Fields().Slice()[i] } // FieldSlice returns a slice of containing all fields/methods of // struct/interface type t. func (t *Type) FieldSlice() []*Field { - var s []*Field - for f, it := IterFields(t); f != nil; f = it.Next() { - s = append(s, f) - } - return s + return t.Fields().Slice() } // SetFields sets struct/interface type t's fields/methods to fields. func (t *Type) SetFields(fields []*Field) { - if t.Etype != TSTRUCT && t.Etype != TINTER { - Fatalf("SetFields: type %v does not have fields", t) - } - var next *Field - for i := len(fields) - 1; i >= 0; i-- { - fields[i].Down = next - next = fields[i] - } - t.Fields = next + t.Fields().Set(fields) } func (t *Type) Size() int64 { @@ -649,11 +676,7 @@ func (t *Type) PtrTo() ssa.Type { } func (t *Type) NumFields() int { - n := 0 - for f, it := IterFields(t); f != nil; f = it.Next() { - n++ - } - return n + return t.Fields().Len() } func (t *Type) FieldType(i int) ssa.Type { return t.Field(i).Type diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index da2f695d3e..9100672e10 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2362,9 +2362,9 @@ func twoarg(n *Node) bool { return true } -func lookdot1(errnode *Node, s *Sym, t *Type, f *Field, dostrcmp int) *Field { +func lookdot1(errnode *Node, s *Sym, t *Type, fs *Fields, dostrcmp int) *Field { var r *Field - for f, it := RawIter(f); f != nil; f = it.Next() { + for _, f := range fs.Slice() { if dostrcmp != 0 && f.Sym.Name == s.Name { return f } @@ -2395,7 +2395,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool { s := n.Right.Sym if t.Etype == TINTER { - f1 := lookdot1(n, s, t, t.Fields, dostrcmp) + f1 := lookdot1(n, s, t, t.Fields(), dostrcmp) if f1 == nil { return false } @@ -2415,7 +2415,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool { } expandmeth(mt) - f2 := lookdot1(n, s, mt, mt.Xmethod, dostrcmp) + f2 := lookdot1(n, s, mt, mt.AllMethods(), dostrcmp) if f2 == nil { return false } @@ -2455,7 +2455,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field { dowidth(t) var f1 *Field if t.Etype == TSTRUCT || t.Etype == TINTER { - f1 = lookdot1(n, s, t, t.Fields, dostrcmp) + f1 = lookdot1(n, s, t, t.Fields(), dostrcmp) } var f2 *Field @@ -2464,7 +2464,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field { if mt != nil { // Use f2->method, not f2->xmethod: adddot has // already inserted all the necessary embedded dots. - f2 = lookdot1(n, s, mt, mt.Method, dostrcmp) + f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp) } } @@ -3103,7 +3103,7 @@ func typecheckcomplit(np **Node) { } } - f := lookdot1(nil, s, t, t.Fields, 0) + f := lookdot1(nil, s, t, t.Fields(), 0) if f == nil { Yyerror("unknown %v field '%v' in struct literal", t, s) continue @@ -3524,8 +3524,8 @@ func copytype(n *Node, t *Type) { if n.Name != nil { t.Vargen = n.Name.Vargen } - t.Method = nil - t.Xmethod = nil + t.methods = Fields{} + t.allMethods = Fields{} t.Nod = nil t.Printed = false t.Deferwidth = false