diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index a9ed774..2a4be1e 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -2152,7 +2152,11 @@
return ir.NewZero(pos, typ)
case exprCompLit:
- return r.compLit()
+ if r.Version().Has(pkgbits.SelectorIndex) {
+ return r.compLitV3()
+ } else {
+ return r.compLit()
+ }
case exprFuncLit:
return r.funcLit()
@@ -3057,6 +3061,118 @@
return lit
}
+func (r *reader) compLitV3() ir.Node {
+ r.Sync(pkgbits.SyncCompLit)
+ pos := r.pos()
+ typ0 := r.typ()
+
+ typ := typ0
+ if typ.IsPtr() {
+ typ = typ.Elem()
+ }
+ if typ.Kind() == types.TFORW {
+ base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
+ }
+ var rtype ir.Node
+ if typ.IsMap() {
+ rtype = r.rtype(pos)
+ }
+
+ n := r.Int()
+ elems := make([]ir.Node, max(n, -n) /* abs(n) */)
+ switch typ.Kind() {
+ default:
+ base.FatalfAt(pos, "unexpected composite literal type: %v", typ)
+ case types.TARRAY:
+ r.arrayElems(elems)
+ case types.TMAP:
+ r.mapElems(elems)
+ case types.TSLICE:
+ r.arrayElems(elems)
+ case types.TSTRUCT:
+ r.structElems(typ, n >= 0, elems)
+ }
+
+ lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems))
+ if rtype != nil {
+ lit := lit.(*ir.CompLitExpr)
+ lit.RType = rtype
+ }
+ if typ0.IsPtr() {
+ lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
+ lit.SetType(typ0)
+ }
+ return lit
+}
+
+func (r *reader) arrayElems(elems []ir.Node) {
+ // some elements may have a key
+ for i := range elems {
+ elemp := &elems[i]
+ if r.Bool() {
+ kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
+ *elemp, elemp = kv, &kv.Value
+ }
+ *elemp = r.expr()
+ }
+}
+
+func (r *reader) mapElems(elems []ir.Node) {
+ // all elements have a key
+ for i := range elems {
+ elemp := &elems[i]
+ kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
+ *elemp, elemp = kv, &kv.Value
+ *elemp = r.expr()
+ }
+}
+
+func (r *reader) structElems(typ *types.Type, valuesOnly bool, elems []ir.Node) {
+ if valuesOnly {
+ for i := range elems {
+ elemp := &elems[i]
+ pos := r.pos()
+ i, index := r.selectorIndex()
+ if index == nil {
+ sk := ir.NewStructKeyExpr(pos, typ.Field(i), nil)
+ *elemp, elemp = sk, &sk.Value
+ } else {
+ sk := ir.NewStructKeyExpr(pos, typ.EmbeddedField(index), nil)
+ *elemp, elemp = sk, &sk.Value
+ }
+ *elemp = r.expr()
+ }
+ return
+ }
+
+ // all elements have a key
+ for i := range elems {
+ elemp := &elems[i]
+ pos := r.pos()
+ i, index := r.selectorIndex()
+ if index == nil {
+ sk := ir.NewStructKeyExpr(pos, typ.Field(i), nil)
+ *elemp, elemp = sk, &sk.Value
+ } else {
+ sk := ir.NewStructKeyExpr(pos, typ.EmbeddedField(index), nil)
+ *elemp, elemp = sk, &sk.Value
+ }
+ *elemp = r.expr()
+ }
+}
+
+func (r *reader) selectorIndex() (i int, index []int) {
+ i = r.Int()
+ if i < 0 {
+ // embedded field
+ index = make([]int, -i)
+ for j := range index {
+ index[j] = r.Int()
+ }
+ }
+ return
+}
+
func (r *reader) funcLit() ir.Node {
r.Sync(pkgbits.SyncFuncLit)
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 05f4483..8a08ffd 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -463,8 +463,8 @@
// writeUnifiedExport writes to `out` the finalized, self-contained
// Unified IR export data file for the current compilation unit.
func writeUnifiedExport(out io.Writer) {
- // Use V2 as the encoded version for aliastypeparams.
- version := pkgbits.V2
+ // Use V3 as the encoded version for struct field selectors.
+ version := pkgbits.V3
l := linker{
pw: pkgbits.NewPkgEncoder(version, base.Debug.SyncFrames),
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index e772328..a4572b4 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -96,8 +96,8 @@
// newPkgWriter returns an initialized pkgWriter for the specified
// package.
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {
- // Use V2 as the encoded version for aliastypeparams.
- version := pkgbits.V2
+ // Use V3 as the encoded version for struct field selectors.
+ version := pkgbits.V3
return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(version, base.Debug.SyncFrames),
@@ -1875,7 +1875,11 @@
case *syntax.CompositeLit:
w.Code(exprCompLit)
- w.compLit(expr)
+ if w.Version().Has(pkgbits.SelectorIndex) {
+ w.compLitV3(expr)
+ } else {
+ w.compLit(expr)
+ }
case *syntax.FuncLit:
w.Code(exprFuncLit)
@@ -2388,6 +2392,97 @@
}
}
+func (w *writer) compLitV3(lit *syntax.CompositeLit) {
+ typ := w.p.typeOf(lit)
+
+ w.Sync(pkgbits.SyncCompLit)
+ w.pos(lit)
+ w.typ(typ)
+
+ if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
+ typ = ptr.Elem()
+ }
+
+ switch typ0 := typ; typ := types2.CoreType(typ).(type) {
+ default:
+ w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
+ case *types2.Array:
+ w.arrayElems(typ.Elem(), lit.ElemList)
+ case *types2.Map:
+ w.rtype(typ0)
+ w.mapElems(typ.Key(), typ.Elem(), lit.ElemList)
+ case *types2.Slice:
+ w.arrayElems(typ.Elem(), lit.ElemList)
+ case *types2.Struct:
+ w.structElems(typ, lit.NKeys == 0, lit.ElemList)
+ }
+}
+
+func (w *writer) arrayElems(elemType types2.Type, elems []syntax.Expr) {
+ // some elements may have a key
+ w.Int(len(elems))
+ for _, elem := range elems {
+ if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
+ w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
+ w.implicitConvExpr(nil, kv.Key)
+ elem = kv.Value
+ }
+ w.implicitConvExpr(elemType, elem)
+ }
+}
+
+func (w *writer) mapElems(keyType, valueType types2.Type, elems []syntax.Expr) {
+ // all elements have a key
+ w.Int(-len(elems))
+ for _, elem := range elems {
+ kv := elem.(*syntax.KeyValueExpr)
+ w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
+ w.implicitConvExpr(keyType, kv.Key)
+ w.implicitConvExpr(valueType, kv.Value)
+ }
+}
+
+func (w *writer) structElems(typ *types2.Struct, valuesOnly bool, elems []syntax.Expr) {
+ n := len(elems)
+ if valuesOnly {
+ // no element has a key
+ w.Int(n)
+ for i, elem := range elems {
+ w.pos(elem)
+ w.Int(i)
+ w.implicitConvExpr(typ.Field(i).Type(), elem)
+ }
+ return
+ }
+ // all elements have a key
+ w.Int(-n)
+ for _, elem := range elems {
+ kv := elem.(*syntax.KeyValueExpr)
+ w.pos(kv.Key) // use position of Key rather than of elem (which has position of ':')
+ // TODO(gri): rather than doing this lookup again, perhaps the index should be recorded by types2
+ fld, index, _ := types2.LookupFieldOrMethod(typ, false, w.p.curpkg, kv.Key.(*syntax.Name).Value)
+ w.selectorIndex(index)
+ w.implicitConvExpr(fld.Type(), kv.Value)
+ }
+}
+
+func (w *writer) selectorIndex(index []int) {
+ n := len(index)
+ assert(n > 0)
+ if n == 1 {
+ i := index[0]
+ assert(i >= 0)
+ w.Int(i)
+ return
+ }
+ // n > 1
+ w.Int(-n) // -n < 0
+ for _, i := range index {
+ assert(i >= 0)
+ w.Int(i)
+ }
+}
+
func (w *writer) funcLit(expr *syntax.FuncLit) {
sig := w.p.typeOf(expr).(*types2.Signature)
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 3fa0b90..b03df1e 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -957,6 +957,15 @@
// Field returns the i'th field of struct type t.
func (t *Type) Field(i int) *Field { return t.Fields()[i] }
+// EmbeddedField returns the embedded field specified by the index sequence.
+func (t *Type) EmbeddedField(index []int) (f *Field) {
+ for _, i := range index {
+ f = t.Field(i)
+ t = f.Type
+ }
+ return
+}
+
// Fields returns a slice of containing all fields of
// a struct type t.
func (t *Type) Fields() []*Field { return t.fields().Slice() }
diff --git a/src/internal/pkgbits/version.go b/src/internal/pkgbits/version.go
index ba664f4..8a0bee8 100644
--- a/src/internal/pkgbits/version.go
+++ b/src/internal/pkgbits/version.go
@@ -28,6 +28,11 @@
// - remove derived info "needed" bool
V2
+ // V3: introduces support for field selectors in struct literals
+ // - field indices are written using w.Int rather than w.Len
+ // - embedded fields start with a negative count of embedded indices
+ V3
+
numVersions = iota
)
@@ -61,6 +66,9 @@
// whether a type was a derived type.
DerivedInfoNeeded
+ // Struct field selectors are encoded as a []int index.
+ SelectorIndex
+
numFields = iota
)
@@ -68,6 +76,7 @@
var introduced = [numFields]Version{
Flags: V1,
AliasTypeParamNames: V2,
+ SelectorIndex: V3,
}
// removed is the version a field was removed in or 0 for fields