From 1f8d4562debf8310910897ea85705cc067baa52d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 11:29:44 -0700 Subject: [PATCH 01/15] cmd/compile: change typecheck.iscmp into ir.Op.IsCmp Change-Id: If89089cbd79b7ff030d856df3a7e6b7862c0f4ec Reviewed-on: https://go-review.googlesource.com/c/go/+/345412 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/ir/node.go | 10 ++++++++++ src/cmd/compile/internal/noder/stencil.go | 2 +- src/cmd/compile/internal/typecheck/expr.go | 6 +----- src/cmd/compile/internal/typecheck/universe.go | 8 -------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go index f071cb78ce5..8784f9ef994 100644 --- a/src/cmd/compile/internal/ir/node.go +++ b/src/cmd/compile/internal/ir/node.go @@ -334,6 +334,16 @@ const ( OEND ) +// IsCmp reports whether op is a comparison operation (==, !=, <, <=, +// >, or >=). +func (op Op) IsCmp() bool { + switch op { + case OEQ, ONE, OLT, OLE, OGT, OGE: + return true + } + return false +} + // Nodes is a pointer to a slice of *Node. // For fields that are not used in most nodes, this is used instead of // a slice to save space. diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index b3ff4b8855c..0c6bb5100c6 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -903,7 +903,7 @@ func (subst *subster) node(n ir.Node) ir.Node { ir.EditChildren(m, edit) m.SetTypecheck(1) - if typecheck.IsCmp(x.Op()) { + if x.Op().IsCmp() { transformCompare(m.(*ir.BinaryExpr)) } else { switch x.Op() { diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go index 7e974dfda86..d83bc65bed6 100644 --- a/src/cmd/compile/internal/typecheck/expr.go +++ b/src/cmd/compile/internal/typecheck/expr.go @@ -77,10 +77,6 @@ func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) { return l, r, t } -func IsCmp(op ir.Op) bool { - return iscmp[op] -} - // tcArith typechecks operands of a binary arithmetic expression. // The result of tcArith MUST be assigned back to original operands, // t is the type of the expression, and should be set by the caller. e.g: @@ -96,7 +92,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) t = r.Type() } aop := ir.OXXX - if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { + if n.Op().IsCmp() && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { // comparison is okay as long as one side is // assignable to the other. convert so they have // the same type. diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go index 54f3c89c245..a7c84dc8d82 100644 --- a/src/cmd/compile/internal/typecheck/universe.go +++ b/src/cmd/compile/internal/typecheck/universe.go @@ -329,14 +329,6 @@ func InitUniverse() { // special okfor[ir.OCAP] = okforcap[:] okfor[ir.OLEN] = okforlen[:] - - // comparison - iscmp[ir.OLT] = true - iscmp[ir.OGT] = true - iscmp[ir.OGE] = true - iscmp[ir.OLE] = true - iscmp[ir.OEQ] = true - iscmp[ir.ONE] = true } func makeErrorInterface() *types.Type { From 3836983779a8f1f1a1b6dc629832e695dcacaf36 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 11:46:24 -0700 Subject: [PATCH 02/15] cmd/compile/internal/types: unexport Type.Extra Not used outside of package types anymore. Let's keep it that. Change-Id: I69b464ac94edaacd219da4210f7b8618e2beaf70 Reviewed-on: https://go-review.googlesource.com/c/go/+/345413 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/types/fmt.go | 4 +- src/cmd/compile/internal/types/size.go | 2 +- src/cmd/compile/internal/types/type.go | 182 ++++++++++---------- src/cmd/compile/internal/types/type_test.go | 19 +- 4 files changed, 103 insertions(+), 104 deletions(-) diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 0824f6d0936..b1b85064007 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -298,7 +298,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type return } if t.Kind() == TSSA { - b.WriteString(t.Extra.(string)) + b.WriteString(t.extra.(string)) return } if t.Kind() == TTUPLE { @@ -309,7 +309,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type } if t.Kind() == TRESULTS { - tys := t.Extra.(*Results).Types + tys := t.extra.(*Results).Types for i, et := range tys { if i > 0 { b.WriteByte(',') diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go index 89391ade683..34db0a0b0cf 100644 --- a/src/cmd/compile/internal/types/size.go +++ b/src/cmd/compile/internal/types/size.go @@ -526,7 +526,7 @@ func CalcSize(t *Type) { w = calcStructOffset(t1, t1.Recvs(), 0, 0) w = calcStructOffset(t1, t1.Params(), w, RegSize) w = calcStructOffset(t1, t1.Results(), w, RegSize) - t1.Extra.(*Func).Argwid = w + t1.extra.(*Func).Argwid = w if w%int64(RegSize) != 0 { base.Warn("bad type %v %d\n", t1, w) } diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 875b0ba82f1..975280753a4 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -139,7 +139,7 @@ var ( // A Type represents a Go type. type Type struct { - // Extra contains extra etype-specific fields. + // extra contains extra etype-specific fields. // As an optimization, those etype-specific structs which contain exactly // one pointer-shaped field are stored as values rather than pointers when possible. // @@ -156,7 +156,7 @@ type Type struct { // TSLICE: Slice // TSSA: string // TTYPEPARAM: *Typeparam - Extra interface{} + extra interface{} // Width is the width of this Type in bytes. Width int64 // valid if Align > 0 @@ -325,11 +325,11 @@ var NoPkg *Pkg = nil func (t *Type) Pkg() *Pkg { switch t.kind { case TFUNC: - return t.Extra.(*Func).pkg + return t.extra.(*Func).pkg case TSTRUCT: - return t.Extra.(*Struct).pkg + return t.extra.(*Struct).pkg case TINTER: - return t.Extra.(*Interface).pkg + return t.extra.(*Interface).pkg default: base.Fatalf("Pkg: unexpected kind: %v", t) return nil @@ -349,7 +349,7 @@ type Map struct { // MapType returns t's extra map-specific fields. func (t *Type) MapType() *Map { t.wantEtype(TMAP) - return t.Extra.(*Map) + return t.extra.(*Map) } // Forward contains Type fields specific to forward types. @@ -361,7 +361,7 @@ type Forward struct { // ForwardType returns t's extra forward-type-specific fields. func (t *Type) ForwardType() *Forward { t.wantEtype(TFORW) - return t.Extra.(*Forward) + return t.extra.(*Forward) } // Func contains Type fields specific to func types. @@ -382,7 +382,7 @@ type Func struct { // FuncType returns t's extra func-specific fields. func (t *Type) FuncType() *Func { t.wantEtype(TFUNC) - return t.Extra.(*Func) + return t.extra.(*Func) } // StructType contains Type fields specific to struct types. @@ -411,7 +411,7 @@ const ( // StructType returns t's extra struct-specific fields. func (t *Type) StructType() *Struct { t.wantEtype(TSTRUCT) - return t.Extra.(*Struct) + return t.extra.(*Struct) } // Interface contains Type fields specific to interface types. @@ -455,7 +455,7 @@ type Chan struct { // ChanType returns t's extra channel-specific fields. func (t *Type) ChanType() *Chan { t.wantEtype(TCHAN) - return t.Extra.(*Chan) + return t.extra.(*Chan) } type Tuple struct { @@ -590,31 +590,31 @@ func New(et Kind) *Type { // TODO(josharian): lazily initialize some of these? switch t.kind { case TMAP: - t.Extra = new(Map) + t.extra = new(Map) case TFORW: - t.Extra = new(Forward) + t.extra = new(Forward) case TFUNC: - t.Extra = new(Func) + t.extra = new(Func) case TSTRUCT: - t.Extra = new(Struct) + t.extra = new(Struct) case TINTER: - t.Extra = new(Interface) + t.extra = new(Interface) case TPTR: - t.Extra = Ptr{} + t.extra = Ptr{} case TCHANARGS: - t.Extra = ChanArgs{} + t.extra = ChanArgs{} case TFUNCARGS: - t.Extra = FuncArgs{} + t.extra = FuncArgs{} case TCHAN: - t.Extra = new(Chan) + t.extra = new(Chan) case TTUPLE: - t.Extra = new(Tuple) + t.extra = new(Tuple) case TRESULTS: - t.Extra = new(Results) + t.extra = new(Results) case TTYPEPARAM: - t.Extra = new(Typeparam) + t.extra = new(Typeparam) case TUNION: - t.Extra = new(Union) + t.extra = new(Union) } return t } @@ -625,7 +625,7 @@ func NewArray(elem *Type, bound int64) *Type { base.Fatalf("NewArray: invalid bound %v", bound) } t := New(TARRAY) - t.Extra = &Array{Elem: elem, Bound: bound} + t.extra = &Array{Elem: elem, Bound: bound} t.SetNotInHeap(elem.NotInHeap()) if elem.HasTParam() { t.SetHasTParam(true) @@ -646,7 +646,7 @@ func NewSlice(elem *Type) *Type { } t := New(TSLICE) - t.Extra = Slice{Elem: elem} + t.extra = Slice{Elem: elem} elem.cache.slice = t if elem.HasTParam() { t.SetHasTParam(true) @@ -674,8 +674,8 @@ func NewChan(elem *Type, dir ChanDir) *Type { func NewTuple(t1, t2 *Type) *Type { t := New(TTUPLE) - t.Extra.(*Tuple).first = t1 - t.Extra.(*Tuple).second = t2 + t.extra.(*Tuple).first = t1 + t.extra.(*Tuple).second = t2 if t1.HasTParam() || t2.HasTParam() { t.SetHasTParam(true) } @@ -687,7 +687,7 @@ func NewTuple(t1, t2 *Type) *Type { func newResults(types []*Type) *Type { t := New(TRESULTS) - t.Extra.(*Results).Types = types + t.extra.(*Results).Types = types return t } @@ -700,7 +700,7 @@ func NewResults(types []*Type) *Type { func newSSA(name string) *Type { t := New(TSSA) - t.Extra = name + t.extra = name return t } @@ -747,7 +747,7 @@ func NewPtr(elem *Type) *Type { } t := New(TPTR) - t.Extra = Ptr{Elem: elem} + t.extra = Ptr{Elem: elem} t.Width = int64(PtrSize) t.Align = uint8(PtrSize) if NewPtrCacheEnabled { @@ -765,14 +765,14 @@ func NewPtr(elem *Type) *Type { // NewChanArgs returns a new TCHANARGS type for channel type c. func NewChanArgs(c *Type) *Type { t := New(TCHANARGS) - t.Extra = ChanArgs{T: c} + t.extra = ChanArgs{T: c} return t } // NewFuncArgs returns a new TFUNCARGS type for func type f. func NewFuncArgs(f *Type) *Type { t := New(TFUNCARGS) - t.Extra = FuncArgs{T: f} + t.extra = FuncArgs{T: f} return t } @@ -811,28 +811,28 @@ func SubstAny(t *Type, types *[]*Type) *Type { elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra = Ptr{Elem: elem} + t.extra = Ptr{Elem: elem} } case TARRAY: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra.(*Array).Elem = elem + t.extra.(*Array).Elem = elem } case TSLICE: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra = Slice{Elem: elem} + t.extra = Slice{Elem: elem} } case TCHAN: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra.(*Chan).Elem = elem + t.extra.(*Chan).Elem = elem } case TMAP: @@ -840,8 +840,8 @@ func SubstAny(t *Type, types *[]*Type) *Type { elem := SubstAny(t.Elem(), types) if key != t.Key() || elem != t.Elem() { t = t.copy() - t.Extra.(*Map).Key = key - t.Extra.(*Map).Elem = elem + t.extra.(*Map).Key = key + t.extra.(*Map).Elem = elem } case TFUNC: @@ -882,26 +882,26 @@ func (t *Type) copy() *Type { // copy any *T Extra fields, to avoid aliasing switch t.kind { case TMAP: - x := *t.Extra.(*Map) - nt.Extra = &x + x := *t.extra.(*Map) + nt.extra = &x case TFORW: - x := *t.Extra.(*Forward) - nt.Extra = &x + x := *t.extra.(*Forward) + nt.extra = &x case TFUNC: - x := *t.Extra.(*Func) - nt.Extra = &x + x := *t.extra.(*Func) + nt.extra = &x case TSTRUCT: - x := *t.Extra.(*Struct) - nt.Extra = &x + x := *t.extra.(*Struct) + nt.extra = &x case TINTER: - x := *t.Extra.(*Interface) - nt.Extra = &x + x := *t.extra.(*Interface) + nt.extra = &x case TCHAN: - x := *t.Extra.(*Chan) - nt.Extra = &x + x := *t.extra.(*Chan) + nt.extra = &x case TARRAY: - x := *t.Extra.(*Array) - nt.Extra = &x + x := *t.extra.(*Array) + nt.extra = &x case TTYPEPARAM: base.Fatalf("typeparam types cannot be copied") case TTUPLE, TSSA, TRESULTS: @@ -970,7 +970,7 @@ var ParamsResults = [2]func(*Type) *Type{ // Key returns the key type of map type t. func (t *Type) Key() *Type { t.wantEtype(TMAP) - return t.Extra.(*Map).Key + return t.extra.(*Map).Key } // Elem returns the type of elements of t. @@ -978,15 +978,15 @@ func (t *Type) Key() *Type { func (t *Type) Elem() *Type { switch t.kind { case TPTR: - return t.Extra.(Ptr).Elem + return t.extra.(Ptr).Elem case TARRAY: - return t.Extra.(*Array).Elem + return t.extra.(*Array).Elem case TSLICE: - return t.Extra.(Slice).Elem + return t.extra.(Slice).Elem case TCHAN: - return t.Extra.(*Chan).Elem + return t.extra.(*Chan).Elem case TMAP: - return t.Extra.(*Map).Elem + return t.extra.(*Map).Elem } base.Fatalf("Type.Elem %s", t.kind) return nil @@ -995,18 +995,18 @@ func (t *Type) Elem() *Type { // ChanArgs returns the channel type for TCHANARGS type t. func (t *Type) ChanArgs() *Type { t.wantEtype(TCHANARGS) - return t.Extra.(ChanArgs).T + return t.extra.(ChanArgs).T } // FuncArgs returns the func type for TFUNCARGS type t. func (t *Type) FuncArgs() *Type { t.wantEtype(TFUNCARGS) - return t.Extra.(FuncArgs).T + return t.extra.(FuncArgs).T } // IsFuncArgStruct reports whether t is a struct representing function parameters or results. func (t *Type) IsFuncArgStruct() bool { - return t.kind == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone + return t.kind == TSTRUCT && t.extra.(*Struct).Funarg != FunargNone } // Methods returns a pointer to the base methods (excluding embedding) for type t. @@ -1037,7 +1037,7 @@ func (t *Type) SetAllMethods(fs []*Field) { // Fields returns the fields of struct type t. func (t *Type) Fields() *Fields { t.wantEtype(TSTRUCT) - return &t.Extra.(*Struct).fields + return &t.extra.(*Struct).fields } // Field returns the i'th field of struct type t. @@ -1091,7 +1091,7 @@ func (t *Type) WidthCalculated() bool { // It includes the receiver, parameters, and results. func (t *Type) ArgWidth() int64 { t.wantEtype(TFUNC) - return t.Extra.(*Func).Argwid + return t.extra.(*Func).Argwid } func (t *Type) Size() int64 { @@ -1234,8 +1234,8 @@ func (t *Type) cmp(x *Type) Cmp { return CMPeq case TSSA: - tname := t.Extra.(string) - xname := x.Extra.(string) + tname := t.extra.(string) + xname := x.extra.(string) // desire fast sorting, not pretty sorting. if len(tname) == len(xname) { if tname == xname { @@ -1252,16 +1252,16 @@ func (t *Type) cmp(x *Type) Cmp { return CMPlt case TTUPLE: - xtup := x.Extra.(*Tuple) - ttup := t.Extra.(*Tuple) + xtup := x.extra.(*Tuple) + ttup := t.extra.(*Tuple) if c := ttup.first.Compare(xtup.first); c != CMPeq { return c } return ttup.second.Compare(xtup.second) case TRESULTS: - xResults := x.Extra.(*Results) - tResults := t.Extra.(*Results) + xResults := x.extra.(*Results) + tResults := t.extra.(*Results) xl, tl := len(xResults.Types), len(tResults.Types) if tl != xl { if tl < xl { @@ -1548,7 +1548,7 @@ func (t *Type) PtrTo() *Type { func (t *Type) NumFields() int { if t.kind == TRESULTS { - return len(t.Extra.(*Results).Types) + return len(t.extra.(*Results).Types) } return t.Fields().Len() } @@ -1556,15 +1556,15 @@ func (t *Type) FieldType(i int) *Type { if t.kind == TTUPLE { switch i { case 0: - return t.Extra.(*Tuple).first + return t.extra.(*Tuple).first case 1: - return t.Extra.(*Tuple).second + return t.extra.(*Tuple).second default: panic("bad tuple index") } } if t.kind == TRESULTS { - return t.Extra.(*Results).Types[i] + return t.extra.(*Results).Types[i] } return t.Field(i).Type } @@ -1577,7 +1577,7 @@ func (t *Type) FieldName(i int) string { func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) - return t.Extra.(*Array).Bound + return t.extra.(*Array).Bound } type componentsIncludeBlankFields bool @@ -1639,15 +1639,15 @@ func (t *Type) SoleComponent() *Type { // The direction will be one of Crecv, Csend, or Cboth. func (t *Type) ChanDir() ChanDir { t.wantEtype(TCHAN) - return t.Extra.(*Chan).Dir + return t.extra.(*Chan).Dir } func (t *Type) IsMemory() bool { - if t == TypeMem || t.kind == TTUPLE && t.Extra.(*Tuple).second == TypeMem { + if t == TypeMem || t.kind == TTUPLE && t.extra.(*Tuple).second == TypeMem { return true } if t.kind == TRESULTS { - if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { + if types := t.extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { return true } } @@ -1699,11 +1699,11 @@ func (t *Type) HasPointers() bool { return !t.Elem().NotInHeap() case TTUPLE: - ttup := t.Extra.(*Tuple) + ttup := t.extra.(*Tuple) return ttup.first.HasPointers() || ttup.second.HasPointers() case TRESULTS: - types := t.Extra.(*Results).Types + types := t.extra.(*Results).Types for _, et := range types { if et.HasPointers() { return true @@ -1781,7 +1781,7 @@ func (t *Type) SetUnderlying(underlying *Type) { // TODO(mdempsky): Fix Type rekinding. t.kind = underlying.kind - t.Extra = underlying.Extra + t.extra = underlying.extra t.Width = underlying.Width t.Align = underlying.Align t.underlying = underlying.underlying @@ -1865,7 +1865,7 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type { if anyBroke(methods) { t.SetBroke(true) } - t.Extra.(*Interface).pkg = pkg + t.extra.(*Interface).pkg = pkg return t } @@ -1874,7 +1874,7 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type { func NewTypeParam(sym *Sym, index int) *Type { t := New(TTYPEPARAM) t.sym = sym - t.Extra.(*Typeparam).index = index + t.extra.(*Typeparam).index = index t.SetHasTParam(true) return t } @@ -1882,25 +1882,25 @@ func NewTypeParam(sym *Sym, index int) *Type { // Index returns the index of the type param within its param list. func (t *Type) Index() int { t.wantEtype(TTYPEPARAM) - return t.Extra.(*Typeparam).index + return t.extra.(*Typeparam).index } // SetIndex sets the index of the type param within its param list. func (t *Type) SetIndex(i int) { t.wantEtype(TTYPEPARAM) - t.Extra.(*Typeparam).index = i + t.extra.(*Typeparam).index = i } // SetBound sets the bound of a typeparam. func (t *Type) SetBound(bound *Type) { t.wantEtype(TTYPEPARAM) - t.Extra.(*Typeparam).bound = bound + t.extra.(*Typeparam).bound = bound } // Bound returns the bound of a typeparam. func (t *Type) Bound() *Type { t.wantEtype(TTYPEPARAM) - return t.Extra.(*Typeparam).bound + return t.extra.(*Typeparam).bound } // NewUnion returns a new union with the specified set of terms (types). If @@ -1910,8 +1910,8 @@ func NewUnion(terms []*Type, tildes []bool) *Type { if len(terms) != len(tildes) { base.Fatalf("Mismatched terms and tildes for NewUnion") } - t.Extra.(*Union).terms = terms - t.Extra.(*Union).tildes = tildes + t.extra.(*Union).terms = terms + t.extra.(*Union).tildes = tildes nt := len(terms) for i := 0; i < nt; i++ { if terms[i].HasTParam() { @@ -1927,14 +1927,14 @@ func NewUnion(terms []*Type, tildes []bool) *Type { // NumTerms returns the number of terms in a union type. func (t *Type) NumTerms() int { t.wantEtype(TUNION) - return len(t.Extra.(*Union).terms) + return len(t.extra.(*Union).terms) } // Term returns ith term of a union type as (term, tilde). If tilde is true, term // represents ~T, rather than just T. func (t *Type) Term(i int) (*Type, bool) { t.wantEtype(TUNION) - u := t.Extra.(*Union) + u := t.extra.(*Union) return u.terms[i], u.tildes[i] } @@ -1995,7 +1995,7 @@ func NewStruct(pkg *Pkg, fields []*Field) *Type { if anyBroke(fields) { t.SetBroke(true) } - t.Extra.(*Struct).pkg = pkg + t.extra.(*Struct).pkg = pkg if fieldsHasTParam(fields) { t.SetHasTParam(true) } diff --git a/src/cmd/compile/internal/types/type_test.go b/src/cmd/compile/internal/types/type_test.go index fe3f380b21f..1fd05b3f5e8 100644 --- a/src/cmd/compile/internal/types/type_test.go +++ b/src/cmd/compile/internal/types/type_test.go @@ -2,26 +2,25 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types_test +package types import ( - "cmd/compile/internal/types" "testing" ) func TestSSACompare(t *testing.T) { - a := []*types.Type{ - types.TypeInvalid, - types.TypeMem, - types.TypeFlags, - types.TypeVoid, - types.TypeInt128, + a := []*Type{ + TypeInvalid, + TypeMem, + TypeFlags, + TypeVoid, + TypeInt128, } for _, x := range a { for _, y := range a { c := x.Compare(y) - if x == y && c != types.CMPeq || x != y && c == types.CMPeq { - t.Errorf("%s compare %s == %d\n", x.Extra, y.Extra, c) + if x == y && c != CMPeq || x != y && c == CMPeq { + t.Errorf("%s compare %s == %d\n", x.extra, y.extra, c) } } } From eb6a07fcf99050c447097a8ff6358c484c0f8715 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 12:05:45 -0700 Subject: [PATCH 03/15] cmd/compile: unexport Type.Vargen This field is only used outside of packages types in two places, and they follow the same pattern. So this CL creates a Type.Setvargen function that they can use instead, so that Type.Vargen can be unexported. A bit clumsy, but it works for now. Change-Id: I7b4f33fac635e2464df2fbc0607ab40902f6f09f Reviewed-on: https://go-review.googlesource.com/c/go/+/345469 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/noder/decl.go | 3 +-- .../compile/internal/typecheck/typecheck.go | 8 +----- src/cmd/compile/internal/types/fmt.go | 4 +-- src/cmd/compile/internal/types/type.go | 25 ++++++++++++++++--- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go index b23dd476008..54a13b498b9 100644 --- a/src/cmd/compile/internal/noder/decl.go +++ b/src/cmd/compile/internal/noder/decl.go @@ -154,8 +154,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { name, obj := g.def(decl.Name) ntyp, otyp := name.Type(), obj.Type() if ir.CurFunc != nil { - typecheck.TypeGen++ - ntyp.Vargen = typecheck.TypeGen + ntyp.SetVargen() } pragmas := g.pragmaFlags(decl.Pragma, typePragmas) diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index db1b11c4cfd..404af5b1b2c 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -1736,11 +1736,6 @@ func CheckMapKeys() { mapqueue = nil } -// TypeGen tracks the number of function-scoped defined types that -// have been declared. It's used to generate unique linker symbols for -// their runtime type descriptors. -var TypeGen int32 - func typecheckdeftype(n *ir.Name) { if base.EnableTrace && base.Flag.LowerT { defer tracePrint("typecheckdeftype", n)(nil) @@ -1748,8 +1743,7 @@ func typecheckdeftype(n *ir.Name) { t := types.NewNamed(n) if n.Curfn != nil { - TypeGen++ - t.Vargen = TypeGen + t.SetVargen() } if n.Pragma()&ir.NotInHeap != 0 { diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index b1b85064007..74ebfad5bb9 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -361,8 +361,8 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type // output too. It seems like it should, but that mode is currently // used in string representation used by reflection, which is // user-visible and doesn't expect this. - if mode == fmtTypeID && t.Vargen != 0 { - fmt.Fprintf(b, "·%d", t.Vargen) + if mode == fmtTypeID && t.vargen != 0 { + fmt.Fprintf(b, "·%d", t.vargen) } return } diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 975280753a4..06348c50946 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -178,7 +178,7 @@ type Type struct { } sym *Sym // symbol containing name, for named types - Vargen int32 // unique name for OTYPE/ONAME + vargen int32 // unique name for OTYPE/ONAME kind Kind // kind of type Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed) @@ -1221,8 +1221,8 @@ func (t *Type) cmp(x *Type) Cmp { if x.sym != nil { // Syms non-nil, if vargens match then equal. - if t.Vargen != x.Vargen { - return cmpForNe(t.Vargen < x.Vargen) + if t.vargen != x.vargen { + return cmpForNe(t.vargen < x.vargen) } return CMPeq } @@ -1768,6 +1768,25 @@ func (t *Type) Obj() Object { return nil } +// typeGen tracks the number of function-scoped defined types that +// have been declared. It's used to generate unique linker symbols for +// their runtime type descriptors. +var typeGen int32 + +// SetVargen assigns a unique generation number to type t, which must +// be a defined type declared within function scope. The generation +// number is used to distinguish it from other similarly spelled +// defined types from the same package. +// +// TODO(mdempsky): Come up with a better solution. +func (t *Type) SetVargen() { + base.Assertf(t.Sym() != nil, "SetVargen on anonymous type %v", t) + base.Assertf(t.vargen == 0, "type %v already has Vargen %v", t, t.vargen) + + typeGen++ + t.vargen = typeGen +} + // SetUnderlying sets the underlying type. SetUnderlying automatically updates any // types that were waiting for this type to be completed. func (t *Type) SetUnderlying(underlying *Type) { From c9e05fdcf7fd49e9b27f8bcb16347d17d04bc4e7 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Tue, 24 Aug 2021 17:35:51 -0700 Subject: [PATCH 04/15] cmd/compile: fix reference to generic type needed by crawler This problem happens when you create a new local type that uses an imported generic type (maybe just by instantiating it), and then that local type needed to be included as part of an export. In that case, the imported generic type is does not have a declaration in the local package, so it is not necessarily created in types1, so the crawler/export doesn't work. To fix this issue, we just need to add a call to g.obj() for the base generic type, to make sure that it will exist if needed later in the compilation or for the crawler during export. Fixes #47514 Change-Id: Ie756578f07ad0007de8a88ae909cf7534a22936e Reviewed-on: https://go-review.googlesource.com/c/go/+/345411 Reviewed-by: Keith Randall Trust: Dan Scales --- src/cmd/compile/internal/noder/types.go | 7 ++++++- test/typeparam/issue47514c.dir/a.go | 5 +++++ test/typeparam/issue47514c.dir/main.go | 10 ++++++++++ test/typeparam/issue47514c.go | 7 +++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/typeparam/issue47514c.dir/a.go create mode 100644 test/typeparam/issue47514c.dir/main.go create mode 100644 test/typeparam/issue47514c.go diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index c9f7c2bbe47..a5a90aacaaf 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -118,9 +118,14 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { return s.Def.Type() } + // Make sure the base generic type exists in type1 (it may + // not yet if we are referecing an imported generic type, as + // opposed to a generic type declared in this package). + _ = g.obj(typ.Orig().Obj()) + // Create a forwarding type first and put it in the g.typs // map, in order to deal with recursive generic types - // (including via method signatures).. Set up the extra + // (including via method signatures). Set up the extra // ntyp information (Def, RParams, which may set // HasTParam) before translating the underlying type // itself, so we handle recursion correctly. diff --git a/test/typeparam/issue47514c.dir/a.go b/test/typeparam/issue47514c.dir/a.go new file mode 100644 index 00000000000..782b1d2a4f8 --- /dev/null +++ b/test/typeparam/issue47514c.dir/a.go @@ -0,0 +1,5 @@ +package a + +type Doer[T any] interface { + Do() T +} diff --git a/test/typeparam/issue47514c.dir/main.go b/test/typeparam/issue47514c.dir/main.go new file mode 100644 index 00000000000..bc1166f761e --- /dev/null +++ b/test/typeparam/issue47514c.dir/main.go @@ -0,0 +1,10 @@ +package main + +import "a" + +func Do[T any](doer a.Doer[T]) { + doer.Do() +} + +func main() { +} diff --git a/test/typeparam/issue47514c.go b/test/typeparam/issue47514c.go new file mode 100644 index 00000000000..76930e5e4f6 --- /dev/null +++ b/test/typeparam/issue47514c.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored From 03db2c24136939416903b284a19905d97ceea40d Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 26 Aug 2021 11:56:52 -0700 Subject: [PATCH 05/15] cmd/compile/internal/types2: implement TypeList.String (debugging support) Change-Id: Iaa203def3dac94a7d5ff6120e89315c3d7977ee1 Reviewed-on: https://go-review.googlesource.com/c/go/+/345471 Trust: Robert Griesemer Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/types2/instantiate.go | 2 +- src/cmd/compile/internal/types2/subst.go | 6 ------ src/cmd/compile/internal/types2/typelists.go | 13 +++++++++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index f9cde24dfc8..4113d248b8f 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -75,7 +75,7 @@ func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posList []syntax.Pos) (res Type) { assert(check != nil) if check.conf.Trace { - check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs)) + check.trace(pos, "-- instantiating %s with %s", typ, NewTypeList(targs)) check.indent++ defer func() { check.indent-- diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index ff8dd13b6d7..7c33e7ade40 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -281,12 +281,6 @@ func instantiatedHash(typ *Named, targs []Type) string { return string(res[:i]) } -func typeListString(list []Type) string { - var buf bytes.Buffer - writeTypeList(&buf, list, nil, nil) - return buf.String() -} - // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) // where an array/slice element is accessed before it is set up. diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go index 3258a5e9f87..c3befb077fc 100644 --- a/src/cmd/compile/internal/types2/typelists.go +++ b/src/cmd/compile/internal/types2/typelists.go @@ -4,6 +4,8 @@ package types2 +import "bytes" + // TParamList holds a list of type parameters. type TParamList struct{ tparams []*TypeParam } @@ -52,6 +54,17 @@ func (l *TypeList) list() []Type { return l.types } +func (l *TypeList) String() string { + if l == nil || len(l.types) == 0 { + return "[]" + } + var buf bytes.Buffer + buf.WriteByte('[') + writeTypeList(&buf, l.types, nil, nil) + buf.WriteByte(']') + return buf.String() +} + // ---------------------------------------------------------------------------- // Implementation From af80af22b507ae23ae04372f30b98f7720c85f8a Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 26 Aug 2021 12:27:06 -0700 Subject: [PATCH 06/15] cmd/compile/internal/types2: do not declare new methods on instantiated types Report an error if an alias is used to declare a method on an instantiated type. Also, when resolving the receiver type, don't use asNamed to avoid premature expansion of the type. Fixes #47968. Change-Id: Ie5acc4cfb1944deaaeeaee98707f31e256f8ef5e Reviewed-on: https://go-review.googlesource.com/c/go/+/345472 Trust: Robert Griesemer Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/types2/signature.go | 8 ++++++- .../types2/testdata/fixedbugs/issue47968.go2 | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2 diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index d28e7b8944a..ddad1f0311e 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -132,7 +132,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // Also: Don't report an error via genericType since it will be reported // again when we type-check the signature. // TODO(gri) maybe the receiver should be marked as invalid instead? - if recv := asNamed(check.genericType(rname, false)); recv != nil { + if recv, _ := check.genericType(rname, false).(*Named); recv != nil { recvTParams = recv.TParams().list() } } @@ -211,6 +211,12 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] switch T := rtyp.(type) { case *Named: T.expand(nil) + // The receiver type may be an instantiated type referred to + // by an alias (which cannot have receiver parameters for now). + if T.TArgs() != nil && sig.RParams() == nil { + check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ) + break + } // spec: "The type denoted by T is called the receiver base type; it must not // be a pointer or interface type and it must be declared in the same package // as the method." diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2 new file mode 100644 index 00000000000..bbbe6805f2d --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2 @@ -0,0 +1,21 @@ +// Copyright 2021 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 p + +type T[P any] struct{} + +func (T[P]) m1() + +type A1 = T + +func (A1[P]) m2() {} + +type A2 = T[int] + +func (A2 /* ERROR cannot define methods on instantiated type T\[int\] */) m3() {} +func (_ /* ERROR cannot define methods on instantiated type T\[int\] */ A2) m4() {} + +func (T[int]) m5() {} // int is the type parameter name, not an instantiation +func (T[* /* ERROR must be an identifier */ int]) m6() {} // syntax error From 967a8017f7d41717335ee020e46e2c55c5c9e96e Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 13:17:56 -0700 Subject: [PATCH 07/15] cmd/compile: move types init code into package types This moves the package types setup code from package typecheck into package types itself. This is a prereq for making types.Type more opaque, because some unit tests depend on being able to init the basic universal types. A few notable details of this CL: 1. Creating the builtin types requires being able to create the ir.Name/ir.OTYPE that represents it, but package types can't depend on package ir. So we add a callback function to handle creating the ir.Name. 2. This CL moves ir.Pkgs.Unsafe to types.UnsafePkg. Package unsafe is part of the language, not like the other ir.Pkgs packages that are purely implementation details. 3. This CL also moves typecheck.FakeRecv to types.FakeRecv, addressing an outstanding TODO. Change-Id: I64de04ce82fbcd1bb59f547e2eea3cda52d89429 Reviewed-on: https://go-review.googlesource.com/c/go/+/345474 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/gc/main.go | 2 +- src/cmd/compile/internal/ir/symtab.go | 1 - src/cmd/compile/internal/noder/import.go | 4 +- src/cmd/compile/internal/noder/noder.go | 2 +- src/cmd/compile/internal/noder/reader.go | 4 +- src/cmd/compile/internal/noder/types.go | 4 +- .../compile/internal/reflectdata/reflect.go | 2 +- src/cmd/compile/internal/typecheck/dcl.go | 7 - src/cmd/compile/internal/typecheck/iexport.go | 6 +- src/cmd/compile/internal/typecheck/iimport.go | 4 +- .../compile/internal/typecheck/universe.go | 144 +----------------- src/cmd/compile/internal/types/fmt.go | 3 + src/cmd/compile/internal/types/type.go | 4 + src/cmd/compile/internal/types/universe.go | 144 ++++++++++++++++++ 14 files changed, 171 insertions(+), 160 deletions(-) create mode 100644 src/cmd/compile/internal/types/universe.go diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 9660ef9dd57..8a365f8f6a1 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -84,7 +84,7 @@ func Main(archInit func(*ssagen.ArchInfo)) { types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin // pseudo-package, accessed by import "unsafe" - ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe") + types.UnsafePkg = types.NewPkg("unsafe", "unsafe") // Pseudo-package that contains the compiler's builtin // declarations for package runtime. These are declared in a diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go index 61727fb1c4b..1e8261810f4 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -68,5 +68,4 @@ var Pkgs struct { Go *types.Pkg Itab *types.Pkg Runtime *types.Pkg - Unsafe *types.Pkg } diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go index 48f0e480284..c26340c9604 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -198,7 +198,7 @@ func importfile(decl *syntax.ImportDecl) *types.Pkg { return nil } - if pkg != ir.Pkgs.Unsafe && pkg.Height >= myheight { + if pkg != types.UnsafePkg && pkg.Height >= myheight { myheight = pkg.Height + 1 } return pkg @@ -231,7 +231,7 @@ func readImportFile(path string, target *ir.Package, check *types2.Checker, pack } if path == "unsafe" { - pkg1, pkg2 = ir.Pkgs.Unsafe, types2.Unsafe + pkg1, pkg2 = types.UnsafePkg, types2.Unsafe // TODO(mdempsky): Investigate if this actually matters. Why would // the linker or runtime care whether a package imported unsafe? diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index e1b485b2b35..61a7f8aad48 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -384,7 +384,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { return } - if ipkg == ir.Pkgs.Unsafe { + if ipkg == types.UnsafePkg { p.importedUnsafe = true } if ipkg.Path == "embed" { diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 985453a1bb3..e7a9d9655b1 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -428,7 +428,7 @@ func (r *reader) interfaceType() *types.Type { pos := r.pos() pkg, sym := r.selector() tpkg = pkg - mtyp := r.signature(pkg, typecheck.FakeRecv()) + mtyp := r.signature(pkg, types.FakeRecv()) methods[i] = types.NewField(pos, sym, mtyp) } for i := range embeddeds { @@ -540,7 +540,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node if tag == objStub { assert(!sym.IsBlank()) switch sym.Pkg { - case types.BuiltinPkg, ir.Pkgs.Unsafe: + case types.BuiltinPkg, types.UnsafePkg: return sym.Def.(ir.Node) } if pri, ok := objReader[sym]; ok { diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index a5a90aacaaf..f5d91097f24 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -22,7 +22,7 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg { case g.self: return types.LocalPkg case types2.Unsafe: - return ir.Pkgs.Unsafe + return types.UnsafePkg } return types.NewPkg(pkg.Path(), pkg.Name()) } @@ -206,7 +206,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { methods := make([]*types.Field, typ.NumExplicitMethods()) for i := range methods { m := typ.ExplicitMethod(i) - mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature)) + mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature)) methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp) } diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 9b9efe04a2e..deb806beace 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -1726,7 +1726,7 @@ func NeedEmit(typ *types.Type) bool { // Local defined type; our responsibility. return true - case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == ir.Pkgs.Unsafe): + case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == types.UnsafePkg): // Package runtime is responsible for including code for builtin // types (predeclared and package unsafe). return true diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go index 472d8d2b8ad..76fc6de6212 100644 --- a/src/cmd/compile/internal/typecheck/dcl.go +++ b/src/cmd/compile/internal/typecheck/dcl.go @@ -314,13 +314,6 @@ func checkembeddedtype(t *types.Type) { } } -// 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 { diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index 75d61157833..82006c32456 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -430,7 +430,7 @@ func (p *iexporter) pushDecl(n *ir.Name) { } // Don't export predeclared declarations. - if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == ir.Pkgs.Unsafe { + if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == types.UnsafePkg { return } @@ -905,7 +905,7 @@ func (w *exportWriter) doTyp(t *types.Type) { // type orderedAbs[T any] T if t.IsTypeParam() && t.Underlying() == t { assert(base.Flag.G > 0) - if s.Pkg == types.BuiltinPkg || s.Pkg == ir.Pkgs.Unsafe { + if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg { base.Fatalf("builtin type missing from typIndex: %v", t) } // Write out the first use of a type param as a qualified ident. @@ -916,7 +916,7 @@ func (w *exportWriter) doTyp(t *types.Type) { } if s != nil { - if s.Pkg == types.BuiltinPkg || s.Pkg == ir.Pkgs.Unsafe { + if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg { base.Fatalf("builtin type missing from typIndex: %v", t) } diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index a1a3ac3e8ad..87ad5d1c54b 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -607,7 +607,7 @@ func (r *importReader) exoticType() *types.Type { case exoticTypeRecv: var rcvr *types.Field if r.bool() { // isFakeRecv - rcvr = fakeRecvField() + rcvr = types.FakeRecv() } else { rcvr = r.exoticParam() } @@ -793,7 +793,7 @@ func (r *importReader) typ1() *types.Type { for i := range methods { pos := r.pos() sym := r.selector() - typ := r.signature(fakeRecvField(), nil) + typ := r.signature(types.FakeRecv(), nil) methods[i] = types.NewField(pos, sym, typ) } diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go index a7c84dc8d82..ebe338e2aac 100644 --- a/src/cmd/compile/internal/typecheck/universe.go +++ b/src/cmd/compile/internal/typecheck/universe.go @@ -29,37 +29,6 @@ var ( okforarith [types.NTYPE]bool ) -var basicTypes = [...]struct { - name string - etype types.Kind -}{ - {"int8", types.TINT8}, - {"int16", types.TINT16}, - {"int32", types.TINT32}, - {"int64", types.TINT64}, - {"uint8", types.TUINT8}, - {"uint16", types.TUINT16}, - {"uint32", types.TUINT32}, - {"uint64", types.TUINT64}, - {"float32", types.TFLOAT32}, - {"float64", types.TFLOAT64}, - {"complex64", types.TCOMPLEX64}, - {"complex128", types.TCOMPLEX128}, - {"bool", types.TBOOL}, - {"string", types.TSTRING}, -} - -var typedefs = [...]struct { - name string - etype types.Kind - sameas32 types.Kind - sameas64 types.Kind -}{ - {"int", types.TINT, types.TINT32, types.TINT64}, - {"uint", types.TUINT, types.TUINT32, types.TUINT64}, - {"uintptr", types.TUINTPTR, types.TUINT32, types.TUINT64}, -} - var builtinFuncs = [...]struct { name string op ir.Op @@ -94,86 +63,12 @@ var unsafeFuncs = [...]struct { // InitUniverse initializes the universe block. func InitUniverse() { - if types.PtrSize == 0 { - base.Fatalf("typeinit before betypeinit") - } - - types.SlicePtrOffset = 0 - types.SliceLenOffset = types.Rnd(types.SlicePtrOffset+int64(types.PtrSize), int64(types.PtrSize)) - types.SliceCapOffset = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize)) - types.SliceSize = types.Rnd(types.SliceCapOffset+int64(types.PtrSize), int64(types.PtrSize)) - - // string is same as slice wo the cap - types.StringSize = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize)) - - for et := types.Kind(0); et < types.NTYPE; et++ { - types.SimType[et] = et - } - - types.Types[types.TANY] = types.New(types.TANY) - types.Types[types.TINTER] = types.NewInterface(types.LocalPkg, nil) - - defBasic := func(kind types.Kind, pkg *types.Pkg, name string) *types.Type { - sym := pkg.Lookup(name) + types.InitTypes(func(sym *types.Sym, typ *types.Type) types.Object { n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym) - t := types.NewBasic(kind, n) - n.SetType(t) + n.SetType(typ) sym.Def = n - if kind != types.TANY { - types.CalcSize(t) - } - return t - } - - for _, s := range &basicTypes { - types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name) - } - - for _, s := range &typedefs { - sameas := s.sameas32 - if types.PtrSize == 8 { - sameas = s.sameas64 - } - types.SimType[s.etype] = sameas - - types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name) - } - - // We create separate byte and rune types for better error messages - // rather than just creating type alias *types.Sym's for the uint8 and - // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false. - // TODO(gri) Should we get rid of this special case (at the cost - // of less informative error messages involving bytes and runes)? - // (Alternatively, we could introduce an OTALIAS node representing - // type aliases, albeit at the cost of having to deal with it everywhere). - types.ByteType = defBasic(types.TUINT8, types.BuiltinPkg, "byte") - types.RuneType = defBasic(types.TINT32, types.BuiltinPkg, "rune") - - // error type - s := types.BuiltinPkg.Lookup("error") - n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s) - types.ErrorType = types.NewNamed(n) - types.ErrorType.SetUnderlying(makeErrorInterface()) - n.SetType(types.ErrorType) - s.Def = n - types.CalcSize(types.ErrorType) - - // comparable type (interface) - s = types.BuiltinPkg.Lookup("comparable") - n = ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s) - types.ComparableType = types.NewNamed(n) - types.ComparableType.SetUnderlying(makeComparableInterface()) - n.SetType(types.ComparableType) - s.Def = n - types.CalcSize(types.ComparableType) - - types.Types[types.TUNSAFEPTR] = defBasic(types.TUNSAFEPTR, ir.Pkgs.Unsafe, "Pointer") - - // simple aliases - types.SimType[types.TMAP] = types.TPTR - types.SimType[types.TCHAN] = types.TPTR - types.SimType[types.TFUNC] = types.TPTR - types.SimType[types.TUNSAFEPTR] = types.TPTR + return n + }) for _, s := range &builtinFuncs { s2 := types.BuiltinPkg.Lookup(s.name) @@ -183,13 +78,13 @@ func InitUniverse() { } for _, s := range &unsafeFuncs { - s2 := ir.Pkgs.Unsafe.Lookup(s.name) + s2 := types.UnsafePkg.Lookup(s.name) def := NewName(s2) def.BuiltinOp = s.op s2.Def = def } - s = types.BuiltinPkg.Lookup("true") + s := types.BuiltinPkg.Lookup("true") s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(true)) s = types.BuiltinPkg.Lookup("false") @@ -219,19 +114,6 @@ func InitUniverse() { s = types.BuiltinPkg.Lookup("iota") s.Def = ir.NewIota(base.Pos, s) - for et := types.TINT8; et <= types.TUINT64; et++ { - types.IsInt[et] = true - } - types.IsInt[types.TINT] = true - types.IsInt[types.TUINT] = true - types.IsInt[types.TUINTPTR] = true - - types.IsFloat[types.TFLOAT32] = true - types.IsFloat[types.TFLOAT64] = true - - types.IsComplex[types.TCOMPLEX64] = true - types.IsComplex[types.TCOMPLEX128] = true - // initialize okfor for et := types.Kind(0); et < types.NTYPE; et++ { if types.IsInt[et] || et == types.TIDEAL { @@ -331,20 +213,6 @@ func InitUniverse() { okfor[ir.OLEN] = okforlen[:] } -func makeErrorInterface() *types.Type { - sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, nil, []*types.Field{ - types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]), - }) - method := types.NewField(src.NoXPos, Lookup("Error"), sig) - return types.NewInterface(types.NoPkg, []*types.Field{method}) -} - -func makeComparableInterface() *types.Type { - sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, nil, nil) - method := types.NewField(src.NoXPos, Lookup("=="), sig) - return types.NewInterface(types.NoPkg, []*types.Field{method}) -} - // DeclareUniverse makes the universe block visible within the current package. func DeclareUniverse() { // Operationally, this is similar to a dot import of builtinpkg, except diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 74ebfad5bb9..2f81c7b2e1c 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -23,6 +23,9 @@ var BuiltinPkg *Pkg // LocalPkg is the package being compiled. var LocalPkg *Pkg +// UnsafePkg is package unsafe. +var UnsafePkg *Pkg + // BlankSym is the blank (_) symbol. var BlankSym *Sym diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 06348c50946..e84e89fd150 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -1738,6 +1738,10 @@ func FakeRecvType() *Type { return recvType } +func FakeRecv() *Field { + return NewField(src.NoXPos, nil, FakeRecvType()) +} + var ( // TSSA types. HasPointers assumes these are pointer-free. TypeInvalid = newSSA("invalid") diff --git a/src/cmd/compile/internal/types/universe.go b/src/cmd/compile/internal/types/universe.go new file mode 100644 index 00000000000..abceecd15d0 --- /dev/null +++ b/src/cmd/compile/internal/types/universe.go @@ -0,0 +1,144 @@ +// 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 types + +import ( + "cmd/compile/internal/base" + "cmd/internal/src" +) + +var basicTypes = [...]struct { + name string + etype Kind +}{ + {"int8", TINT8}, + {"int16", TINT16}, + {"int32", TINT32}, + {"int64", TINT64}, + {"uint8", TUINT8}, + {"uint16", TUINT16}, + {"uint32", TUINT32}, + {"uint64", TUINT64}, + {"float32", TFLOAT32}, + {"float64", TFLOAT64}, + {"complex64", TCOMPLEX64}, + {"complex128", TCOMPLEX128}, + {"bool", TBOOL}, + {"string", TSTRING}, +} + +var typedefs = [...]struct { + name string + etype Kind + sameas32 Kind + sameas64 Kind +}{ + {"int", TINT, TINT32, TINT64}, + {"uint", TUINT, TUINT32, TUINT64}, + {"uintptr", TUINTPTR, TUINT32, TUINT64}, +} + +func InitTypes(defTypeName func(sym *Sym, typ *Type) Object) { + if PtrSize == 0 { + base.Fatalf("typeinit before betypeinit") + } + + SlicePtrOffset = 0 + SliceLenOffset = Rnd(SlicePtrOffset+int64(PtrSize), int64(PtrSize)) + SliceCapOffset = Rnd(SliceLenOffset+int64(PtrSize), int64(PtrSize)) + SliceSize = Rnd(SliceCapOffset+int64(PtrSize), int64(PtrSize)) + + // string is same as slice wo the cap + StringSize = Rnd(SliceLenOffset+int64(PtrSize), int64(PtrSize)) + + for et := Kind(0); et < NTYPE; et++ { + SimType[et] = et + } + + Types[TANY] = New(TANY) + Types[TINTER] = NewInterface(LocalPkg, nil) + + defBasic := func(kind Kind, pkg *Pkg, name string) *Type { + typ := New(kind) + obj := defTypeName(pkg.Lookup(name), typ) + typ.sym = obj.Sym() + typ.nod = obj + if kind != TANY { + CheckSize(typ) + } + return typ + } + + for _, s := range &basicTypes { + Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name) + } + + for _, s := range &typedefs { + sameas := s.sameas32 + if PtrSize == 8 { + sameas = s.sameas64 + } + SimType[s.etype] = sameas + + Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name) + } + + // We create separate byte and rune types for better error messages + // rather than just creating type alias *Sym's for the uint8 and + // int32 Hence, (bytetype|runtype).Sym.isAlias() is false. + // TODO(gri) Should we get rid of this special case (at the cost + // of less informative error messages involving bytes and runes)? + // (Alternatively, we could introduce an OTALIAS node representing + // type aliases, albeit at the cost of having to deal with it everywhere). + ByteType = defBasic(TUINT8, BuiltinPkg, "byte") + RuneType = defBasic(TINT32, BuiltinPkg, "rune") + + // error type + DeferCheckSize() + ErrorType = defBasic(TFORW, BuiltinPkg, "error") + ErrorType.SetUnderlying(makeErrorInterface()) + ResumeCheckSize() + + // comparable type (interface) + DeferCheckSize() + ComparableType = defBasic(TFORW, BuiltinPkg, "comparable") + ComparableType.SetUnderlying(makeComparableInterface()) + ResumeCheckSize() + + Types[TUNSAFEPTR] = defBasic(TUNSAFEPTR, UnsafePkg, "Pointer") + + // simple aliases + SimType[TMAP] = TPTR + SimType[TCHAN] = TPTR + SimType[TFUNC] = TPTR + SimType[TUNSAFEPTR] = TPTR + + for et := TINT8; et <= TUINT64; et++ { + IsInt[et] = true + } + IsInt[TINT] = true + IsInt[TUINT] = true + IsInt[TUINTPTR] = true + + IsFloat[TFLOAT32] = true + IsFloat[TFLOAT64] = true + + IsComplex[TCOMPLEX64] = true + IsComplex[TCOMPLEX128] = true +} + +func makeErrorInterface() *Type { + sig := NewSignature(NoPkg, FakeRecv(), nil, nil, []*Field{ + NewField(src.NoXPos, nil, Types[TSTRING]), + }) + method := NewField(src.NoXPos, LocalPkg.Lookup("Error"), sig) + return NewInterface(NoPkg, []*Field{method}) +} + +func makeComparableInterface() *Type { + sig := NewSignature(NoPkg, FakeRecv(), nil, nil, nil) + method := NewField(src.NoXPos, LocalPkg.Lookup("=="), sig) + return NewInterface(NoPkg, []*Field{method}) +} From f153b6739b30c1e99f8646f2dafc77a92204f10d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 14:16:24 -0700 Subject: [PATCH 08/15] cmd/compile: use typecheck.InitUniverse in unit tests Rather than ad hoc setting up the universe, just initialize it properly. Change-Id: I18484b952321f55eb3e1e48fd383068a4ee75f66 Reviewed-on: https://go-review.googlesource.com/c/go/+/345475 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/ssa/export_test.go | 35 +++++---------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 6d3c0f3ccbd..c4e87ec7d0f 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -5,14 +5,16 @@ package ssa import ( + "testing" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/obj/arm64" "cmd/internal/obj/s390x" "cmd/internal/obj/x86" "cmd/internal/src" - "testing" ) var CheckFunc = checkFunc @@ -104,33 +106,12 @@ func (d TestFrontend) MyImportPath() string { var testTypes Types func init() { - // Initialize just enough of the universe and the types package to make our tests function. - // TODO(josharian): move universe initialization to the types package, - // so this test setup can share it. + // TODO(mdempsky): Push into types.InitUniverse or typecheck.InitUniverse. + types.PtrSize = 8 + types.RegSize = 8 + types.MaxWidth = 1 << 50 - for _, typ := range [...]struct { - width int64 - et types.Kind - }{ - {1, types.TINT8}, - {1, types.TUINT8}, - {1, types.TBOOL}, - {2, types.TINT16}, - {2, types.TUINT16}, - {4, types.TINT32}, - {4, types.TUINT32}, - {4, types.TFLOAT32}, - {4, types.TFLOAT64}, - {8, types.TUINT64}, - {8, types.TINT64}, - {8, types.TINT}, - {8, types.TUINTPTR}, - } { - t := types.New(typ.et) - t.Width = typ.width - t.Align = uint8(typ.width) - types.Types[typ.et] = t - } + typecheck.InitUniverse() testTypes.SetTypPtrs() } From e7eee5e265440b0ed3b76093ccdfd776facc767a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 15:30:40 -0700 Subject: [PATCH 09/15] cmd/compile: remove ssagen/pgen_test.go This test was written in 2015, and hasn't had any meaningful changes to it since. However, it's very ad hoc and pokes into internals that it shouldn't, and it keeps getting in the way of more meaningful refactorings. It doesn't even catch bugs; e.g., it didn't catch that cmpstackvarlt wasn't asymmetric (CL 19778), and a bunch of its test cases are bogus because we never actually use it to sort ir.PFUNC nodes (assert added in this CL). The compiler is woefully lacking in unit tests like these, but I strongly feel this test is more of a hinderance than a help at the moment. Change-Id: I3ac7a3e533947aa95f325626c6eec13a43789f9d Reviewed-on: https://go-review.googlesource.com/c/go/+/345478 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/ssagen/pgen.go | 17 +- src/cmd/compile/internal/ssagen/pgen_test.go | 209 ------------------- 2 files changed, 16 insertions(+), 210 deletions(-) delete mode 100644 src/cmd/compile/internal/ssagen/pgen_test.go diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go index 93157bfa11b..0ddf11b426b 100644 --- a/src/cmd/compile/internal/ssagen/pgen.go +++ b/src/cmd/compile/internal/ssagen/pgen.go @@ -75,7 +75,22 @@ func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // allocate space. In particular, it excludes arguments and results, which are in // the callers frame. func needAlloc(n *ir.Name) bool { - return n.Class == ir.PAUTO || n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters() + if n.Op() != ir.ONAME { + base.FatalfAt(n.Pos(), "%v has unexpected Op %v", n, n.Op()) + } + + switch n.Class { + case ir.PAUTO: + return true + case ir.PPARAM: + return false + case ir.PPARAMOUT: + return n.IsOutputParamInRegisters() + + default: + base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class) + return false + } } func (s *ssafn) AllocFrame(f *ssa.Func) { diff --git a/src/cmd/compile/internal/ssagen/pgen_test.go b/src/cmd/compile/internal/ssagen/pgen_test.go deleted file mode 100644 index 69ed8ad74e9..00000000000 --- a/src/cmd/compile/internal/ssagen/pgen_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2015 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 ssagen - -import ( - "reflect" - "sort" - "testing" - - "cmd/compile/internal/ir" - "cmd/compile/internal/typecheck" - "cmd/compile/internal/types" - "cmd/internal/src" -) - -func typeWithoutPointers() *types.Type { - return types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(src.NoXPos, nil, types.New(types.TINT)), - }) -} - -func typeWithPointers() *types.Type { - return types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(src.NoXPos, nil, types.NewPtr(types.New(types.TINT))), - }) -} - -func markUsed(n *ir.Name) *ir.Name { - n.SetUsed(true) - return n -} - -func markNeedZero(n *ir.Name) *ir.Name { - n.SetNeedzero(true) - return n -} - -// Test all code paths for cmpstackvarlt. -func TestCmpstackvar(t *testing.T) { - nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name { - if s == nil { - s = &types.Sym{Name: "."} - } - n := typecheck.NewName(s) - n.SetType(t) - n.SetFrameOffset(xoffset) - n.Class = cl - return n - } - testdata := []struct { - a, b *ir.Name - lt bool - }{ - { - nod(0, nil, nil, ir.PAUTO), - nod(0, nil, nil, ir.PFUNC), - false, - }, - { - nod(0, nil, nil, ir.PFUNC), - nod(0, nil, nil, ir.PAUTO), - true, - }, - { - nod(0, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - true, - }, - { - nod(20, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - false, - }, - { - nod(10, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - false, - }, - { - nod(10, nil, nil, ir.PPARAM), - nod(20, nil, nil, ir.PPARAMOUT), - true, - }, - { - nod(10, nil, nil, ir.PPARAMOUT), - nod(20, nil, nil, ir.PPARAM), - true, - }, - { - markUsed(nod(0, nil, nil, ir.PAUTO)), - nod(0, nil, nil, ir.PAUTO), - true, - }, - { - nod(0, nil, nil, ir.PAUTO), - markUsed(nod(0, nil, nil, ir.PAUTO)), - false, - }, - { - nod(0, typeWithoutPointers(), nil, ir.PAUTO), - nod(0, typeWithPointers(), nil, ir.PAUTO), - false, - }, - { - nod(0, typeWithPointers(), nil, ir.PAUTO), - nod(0, typeWithoutPointers(), nil, ir.PAUTO), - true, - }, - { - markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)), - nod(0, &types.Type{}, nil, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, nil, ir.PAUTO), - markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)), - false, - }, - { - nod(0, &types.Type{Width: 1}, nil, ir.PAUTO), - nod(0, &types.Type{Width: 2}, nil, ir.PAUTO), - false, - }, - { - nod(0, &types.Type{Width: 2}, nil, ir.PAUTO), - nod(0, &types.Type{Width: 1}, nil, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - false, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - false, - }, - } - for _, d := range testdata { - got := cmpstackvarlt(d.a, d.b) - if got != d.lt { - t.Errorf("want %v < %v", d.a, d.b) - } - // If we expect a < b to be true, check that b < a is false. - if d.lt && cmpstackvarlt(d.b, d.a) { - t.Errorf("unexpected %v < %v", d.b, d.a) - } - } -} - -func TestStackvarSort(t *testing.T) { - nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name { - n := typecheck.NewName(s) - n.SetType(t) - n.SetFrameOffset(xoffset) - n.Class = cl - return n - } - inp := []*ir.Name{ - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC), - markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - } - want := []*ir.Name{ - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC), - markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO), - } - sort.Sort(byStackVar(inp)) - if !reflect.DeepEqual(want, inp) { - t.Error("sort failed") - for i := range inp { - g := inp[i] - w := want[i] - eq := reflect.DeepEqual(w, g) - if !eq { - t.Log(i, w, g) - } - } - } -} From 62f88b6dc8782cedf1a945e4992abda3bc0ad67d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 15:54:44 -0700 Subject: [PATCH 10/15] cmd/compile: add types.RecalcSize This is the only case where Align is assigned outside of package types. Rather than adding a SetAlign method, adding a RecalcSize function is a bit more descriptive. Change-Id: I1b3c01ebd0e41183665baa63c926592865bbbd0b Reviewed-on: https://go-review.googlesource.com/c/go/+/345479 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/noder/func.go | 3 +-- src/cmd/compile/internal/types/size.go | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go index 702138157c5..6077b348a5c 100644 --- a/src/cmd/compile/internal/noder/func.go +++ b/src/cmd/compile/internal/noder/func.go @@ -37,8 +37,7 @@ func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, // calculated its size, including parameter offsets. Now that we've // created the parameter Names, force a recalculation to ensure // their offsets are correct. - typ.Align = 0 - types.CalcSize(typ) + types.RecalcSize(typ) if block != nil { typecheck.DeclContext = ir.PAUTO diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go index 34db0a0b0cf..2546f0e2d11 100644 --- a/src/cmd/compile/internal/types/size.go +++ b/src/cmd/compile/internal/types/size.go @@ -562,6 +562,14 @@ func CalcStructSize(s *Type) { s.Width = calcStructOffset(s, s, 0, 1) // sets align } +// RecalcSize is like CalcSize, but recalculates t's size even if it +// has already been calculated before. It does not recalculate other +// types. +func RecalcSize(t *Type) { + t.Align = 0 + CalcSize(t) +} + // when a type's width should be known, we call CheckSize // to compute it. during a declaration like // From c92759978372ffc354f83e08b1dd6fa496e9b545 Mon Sep 17 00:00:00 2001 From: wangyuntao Date: Fri, 16 Jul 2021 10:29:05 +0000 Subject: [PATCH 11/15] cmd/compile: eliminate repetitive code Change-Id: I02c8b65f7c1c1606c9964ab6c54d5ab5f1b444a5 GitHub-Last-Rev: 3d740b9ac129d38981ee295456d2d7f803a79b77 GitHub-Pull-Request: golang/go#47242 Reviewed-on: https://go-review.googlesource.com/c/go/+/334990 Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky Trust: Cuong Manh Le --- src/cmd/compile/internal/ir/scc.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cmd/compile/internal/ir/scc.go b/src/cmd/compile/internal/ir/scc.go index 2cfceaa1f62..a42951c1dda 100644 --- a/src/cmd/compile/internal/ir/scc.go +++ b/src/cmd/compile/internal/ir/scc.go @@ -116,12 +116,11 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 { var i int for i = len(v.stack) - 1; i >= 0; i-- { x := v.stack[i] + v.nodeID[x] = ^uint32(0) if x == n { break } - v.nodeID[x] = ^uint32(0) } - v.nodeID[n] = ^uint32(0) block := v.stack[i:] // Run escape analysis on this set of functions. v.stack = v.stack[:i] From d7e2e2ec2b2a11c2bfb98a27c03d0dccba62c4fb Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Wed, 18 Aug 2021 06:28:40 -0700 Subject: [PATCH 12/15] cmd/compile: delay fillinMethods to deal with mutually-recursive types We need to delay fillinMethods until we get to a top-level type, so we know all the TFORW types have been filled in, and we can do the substitutions required by fillinMethods. Fixes #47710 Change-Id: I298de7e7753ed31a2c2b1ff04f35177a8afc7a66 Reviewed-on: https://go-review.googlesource.com/c/go/+/345149 Trust: Dan Scales Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/irgen.go | 8 + src/cmd/compile/internal/noder/types.go | 150 ++++++++++-------- .../compile/internal/reflectdata/reflect.go | 2 +- test/typeparam/issue47710.go | 19 +++ 4 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 test/typeparam/issue47710.go diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 29882eb7731..d53c254001c 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -149,6 +149,9 @@ type irgen struct { // statements yet. exprStmtOK bool + // types which we need to finish, by doing g.fillinMethods. + typesToFinalize []*typeDelayInfo + // Fully-instantiated generic types whose methods should be instantiated instTypeList []*types.Type @@ -184,6 +187,11 @@ type delayInfo struct { off int } +type typeDelayInfo struct { + typ *types2.Named + ntyp *types.Type +} + func (g *irgen) generate(noders []*noder) { types.LocalPkg.Name = g.self.Name() types.LocalPkg.Height = g.self.Height() diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index f5d91097f24..e00a3a5da91 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -35,6 +35,16 @@ func (g *irgen) typ(typ types2.Type) *types.Type { types.DeferCheckSize() res := g.typ1(typ) types.ResumeCheckSize() + + // Finish up any types on typesToFinalize, now that we are at the top of a + // fully-defined (possibly recursive) type. fillinMethods could create more + // types to finalize. + for len(g.typesToFinalize) > 0 { + l := len(g.typesToFinalize) + info := g.typesToFinalize[l-1] + g.typesToFinalize = g.typesToFinalize[:l-1] + g.fillinMethods(info.typ, info.ntyp) + } return res } @@ -151,10 +161,19 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { ntyp.SetRParams(rparams) //fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam()) - ntyp.SetUnderlying(g.typ1(typ.Underlying())) - g.fillinMethods(typ, ntyp) // Save the symbol for the base generic type. ntyp.OrigSym = g.pkg(typ.Obj().Pkg()).Lookup(typ.Obj().Name()) + ntyp.SetUnderlying(g.typ1(typ.Underlying())) + if typ.NumMethods() != 0 { + // Save a delayed call to g.fillinMethods() (once + // potentially recursive types have been fully + // resolved). + g.typesToFinalize = append(g.typesToFinalize, + &typeDelayInfo{ + typ: typ, + ntyp: ntyp, + }) + } return ntyp } obj := g.obj(typ.Obj()) @@ -266,76 +285,75 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { } } -// fillinMethods fills in the method name nodes and types for a defined type. This -// is needed for later typechecking when looking up methods of instantiated types, -// and for actually generating the methods for instantiated types. +// fillinMethods fills in the method name nodes and types for a defined type with at +// least one method. This is needed for later typechecking when looking up methods of +// instantiated types, and for actually generating the methods for instantiated +// types. func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { - if typ.NumMethods() != 0 { - targs2 := typ.TArgs() - targs := make([]*types.Type, targs2.Len()) - for i := range targs { - targs[i] = g.typ1(targs2.At(i)) - } + targs2 := typ.TArgs() + targs := make([]*types.Type, targs2.Len()) + for i := range targs { + targs[i] = g.typ1(targs2.At(i)) + } - methods := make([]*types.Field, typ.NumMethods()) - for i := range methods { - m := typ.Method(i) - recvType := deref2(types2.AsSignature(m.Type()).Recv().Type()) - var meth *ir.Name - if m.Pkg() != g.self { - // Imported methods cannot be loaded by name (what - // g.obj() does) - they must be loaded via their - // type. - meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name) + methods := make([]*types.Field, typ.NumMethods()) + for i := range methods { + m := typ.Method(i) + recvType := deref2(types2.AsSignature(m.Type()).Recv().Type()) + var meth *ir.Name + if m.Pkg() != g.self { + // Imported methods cannot be loaded by name (what + // g.obj() does) - they must be loaded via their + // type. + meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name) + } else { + meth = g.obj(m) + } + if recvType != types2.Type(typ) { + // Unfortunately, meth is the type of the method of the + // generic type, so we have to do a substitution to get + // the name/type of the method of the instantiated type, + // using m.Type().RParams() and typ.TArgs() + inst2 := instTypeName2("", typ.TArgs()) + name := meth.Sym().Name + i1 := strings.Index(name, "[") + i2 := strings.Index(name[i1:], "]") + assert(i1 >= 0 && i2 >= 0) + // Generate the name of the instantiated method. + name = name[0:i1] + inst2 + name[i1+i2+1:] + newsym := meth.Sym().Pkg.Lookup(name) + var meth2 *ir.Name + if newsym.Def != nil { + meth2 = newsym.Def.(*ir.Name) } else { - meth = g.obj(m) - } - if recvType != types2.Type(typ) { - // Unfortunately, meth is the type of the method of the - // generic type, so we have to do a substitution to get - // the name/type of the method of the instantiated type, - // using m.Type().RParams() and typ.TArgs() - inst2 := instTypeName2("", typ.TArgs()) - name := meth.Sym().Name - i1 := strings.Index(name, "[") - i2 := strings.Index(name[i1:], "]") - assert(i1 >= 0 && i2 >= 0) - // Generate the name of the instantiated method. - name = name[0:i1] + inst2 + name[i1+i2+1:] - newsym := meth.Sym().Pkg.Lookup(name) - var meth2 *ir.Name - if newsym.Def != nil { - meth2 = newsym.Def.(*ir.Name) - } else { - meth2 = ir.NewNameAt(meth.Pos(), newsym) - rparams := types2.AsSignature(m.Type()).RParams() - tparams := make([]*types.Type, rparams.Len()) - for i := range tparams { - tparams[i] = g.typ1(rparams.At(i)) - } - assert(len(tparams) == len(targs)) - ts := typecheck.Tsubster{ - Tparams: tparams, - Targs: targs, - } - // Do the substitution of the type - meth2.SetType(ts.Typ(meth.Type())) - // Add any new fully instantiated types - // seen during the substitution to - // g.instTypeList. - g.instTypeList = append(g.instTypeList, ts.InstTypeList...) - newsym.Def = meth2 + meth2 = ir.NewNameAt(meth.Pos(), newsym) + rparams := types2.AsSignature(m.Type()).RParams() + tparams := make([]*types.Type, rparams.Len()) + for i := range tparams { + tparams[i] = g.typ1(rparams.At(i)) } - meth = meth2 + assert(len(tparams) == len(targs)) + ts := typecheck.Tsubster{ + Tparams: tparams, + Targs: targs, + } + // Do the substitution of the type + meth2.SetType(ts.Typ(meth.Type())) + // Add any new fully instantiated types + // seen during the substitution to + // g.instTypeList. + g.instTypeList = append(g.instTypeList, ts.InstTypeList...) + newsym.Def = meth2 } - methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) - methods[i].Nname = meth - } - ntyp.Methods().Set(methods) - if !ntyp.HasTParam() && !ntyp.HasShape() { - // Generate all the methods for a new fully-instantiated type. - g.instTypeList = append(g.instTypeList, ntyp) + meth = meth2 } + methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) + methods[i].Nname = meth + } + ntyp.Methods().Set(methods) + if !ntyp.HasTParam() && !ntyp.HasShape() { + // Generate all the methods for a new fully-instantiated type. + g.instTypeList = append(g.instTypeList, ntyp) } } diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index deb806beace..ca48db03f5d 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -927,7 +927,7 @@ func formalType(t *types.Type) *types.Type { func writeType(t *types.Type) *obj.LSym { t = formalType(t) - if t.IsUntyped() { + if t.IsUntyped() || t.HasTParam() { base.Fatalf("writeType %v", t) } diff --git a/test/typeparam/issue47710.go b/test/typeparam/issue47710.go new file mode 100644 index 00000000000..0882cb41377 --- /dev/null +++ b/test/typeparam/issue47710.go @@ -0,0 +1,19 @@ +// compile -G=3 + +// Copyright 2021 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 p + +type FooType[t any] interface { + Foo(BarType[t]) +} +type BarType[t any] interface { + Int(IntType[t]) FooType[int] +} + +type IntType[t any] int + +func (n IntType[t]) Foo(BarType[t]) {} +func (n IntType[_]) String() {} From d350a6653252fe1f698ac00be99964fa31d06e89 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 26 Aug 2021 16:57:06 -0700 Subject: [PATCH 13/15] cmd/compile: eagerly CalcStructSize for synthetic ABI types The next CL is going to replace Type.Width with Type.Size(), but Type.Size() isn't safe to call concurrently. So this CL calls CalcStructSize, which *is* allowed to be used concurrently, but then it's the caller's responsibility to ensure it's called right after NewStruct. Change-Id: If9cd81650ccb3a867b4449af757375fa56227901 Reviewed-on: https://go-review.googlesource.com/c/go/+/345483 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/abi/abiutils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index d657ddc867b..24f34b82633 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -722,14 +722,17 @@ func setup() { types.NewField(nxp, fname("len"), ui), types.NewField(nxp, fname("cap"), ui), }) + types.CalcStructSize(synthSlice) synthString = types.NewStruct(types.NoPkg, []*types.Field{ types.NewField(nxp, fname("data"), unsp), types.NewField(nxp, fname("len"), ui), }) + types.CalcStructSize(synthString) synthIface = types.NewStruct(types.NoPkg, []*types.Field{ types.NewField(nxp, fname("f1"), unsp), types.NewField(nxp, fname("f2"), unsp), }) + types.CalcStructSize(synthIface) }) } From 2c60a99f723e779a39664b5f12cb41878fd4a700 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 26 Aug 2021 14:52:32 -0700 Subject: [PATCH 14/15] cmd/compile/internal/syntax: make valid type parameter list in presence of errors Make sure the parser fills in names and types for type parameter lists, even in the case of errors. While at it, adjust some of the test functions to accept generic code and report all syntax errors. Added offending source as test for types2. Fixes #47996. Change-Id: I449bcf5e2cb80fa2a24cdd3945f484bfca218a06 Reviewed-on: https://go-review.googlesource.com/c/go/+/345476 Trust: Robert Griesemer Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/syntax/dumper_test.go | 6 +----- src/cmd/compile/internal/syntax/parser.go | 5 +---- src/cmd/compile/internal/syntax/printer_test.go | 6 +----- .../internal/types2/testdata/fixedbugs/issue47996.go2 | 8 ++++++++ 4 files changed, 11 insertions(+), 14 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go index 22680dce786..033283a3528 100644 --- a/src/cmd/compile/internal/syntax/dumper_test.go +++ b/src/cmd/compile/internal/syntax/dumper_test.go @@ -13,11 +13,7 @@ func TestDump(t *testing.T) { t.Skip("skipping test in short mode") } - // provide a no-op error handler so parsing doesn't stop after first error - ast, err := ParseFile(*src_, func(error) {}, nil, CheckBranches) - if err != nil { - t.Error(err) - } + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, CheckBranches|AllowGenerics) if ast != nil { Fdump(testOut(), ast) diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index fd97279f9d7..3d1ca9d6d47 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -1924,7 +1924,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* } // distribute parameter types (len(list) > 0) - if named == 0 { + if named == 0 && !requireNames { // all unnamed => found names are named types for _, par := range list { if typ := par.Name; typ != nil { @@ -1932,9 +1932,6 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* par.Name = nil } } - if requireNames { - p.syntaxErrorAt(list[0].Type.Pos(), "type parameters must be named") - } } else if named != len(list) { // some named => all must have names and types var pos Pos // left-most error position (or unknown) diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index ec4b1de573f..638e6d4a9dc 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -18,11 +18,7 @@ func TestPrint(t *testing.T) { t.Skip("skipping test in short mode") } - // provide a no-op error handler so parsing doesn't stop after first error - ast, err := ParseFile(*src_, func(error) {}, nil, 0) - if err != nil { - t.Error(err) - } + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) if ast != nil { Fprint(testOut(), ast, LineForm) diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 new file mode 100644 index 00000000000..56e90942ab6 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 @@ -0,0 +1,8 @@ +// Copyright 2021 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 p + +// don't crash +func T /* ERROR missing */ [P /* ERROR named */ ] m /* ERROR m */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ From 67f7e16bcce0a3e68bf92e233ea16c8a9d2ac07a Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 26 Aug 2021 20:26:32 -0700 Subject: [PATCH 15/15] encoding/gob: optimize decoding of []byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reflect.Value.Slice method unfortunately allocates every time since it needs to place the slice header on the heap. This is silly since gob immediately stores the result back into slice. Instead, use the reflect.Value.SetLen method. DecodeBytesSlice 75.0µs ± 2% 35.2µs ± 6% -53.02% Change-Id: I3ca0529d01bf978f2b76e215f52d369f458951ef Reviewed-on: https://go-review.googlesource.com/c/go/+/345572 Trust: Joe Tsai Run-TryBot: Joe Tsai TryBot-Result: Go Bot Reviewed-by: Rob Pike --- src/encoding/gob/decode.go | 2 +- src/encoding/gob/timing_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go index d2f6c749b1b..4ef9ef7243e 100644 --- a/src/encoding/gob/decode.go +++ b/src/encoding/gob/decode.go @@ -376,7 +376,7 @@ func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) { if value.Cap() < n { value.Set(reflect.MakeSlice(value.Type(), n, n)) } else { - value.Set(value.Slice(0, n)) + value.SetLen(n) } if _, err := state.b.Read(value.Bytes()); err != nil { errorf("error decoding []byte: %s", err) diff --git a/src/encoding/gob/timing_test.go b/src/encoding/gob/timing_test.go index 3478bd247ed..ceb21c4107b 100644 --- a/src/encoding/gob/timing_test.go +++ b/src/encoding/gob/timing_test.go @@ -280,6 +280,14 @@ func BenchmarkDecodeStringSlice(b *testing.B) { benchmarkDecodeSlice(b, a) } +func BenchmarkDecodeBytesSlice(b *testing.B) { + a := make([][]byte, 1000) + for i := range a { + a[i] = []byte("now is the time") + } + benchmarkDecodeSlice(b, a) +} + func BenchmarkDecodeInterfaceSlice(b *testing.B) { a := make([]interface{}, 1000) for i := range a {