Skip to content

Commit

Permalink
[dev.unified] cmd/compile: add method expressions to dictionaries
Browse files Browse the repository at this point in the history
This CL changes method expressions that use derived-type receiver
parameters to use dictionary lookups.

Change-Id: Iacd09b6d77a2d3000438ec8bc9b5af2a0b068aa7
Reviewed-on: https://go-review.googlesource.com/c/go/+/419455
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
mdempsky committed Jul 25, 2022
1 parent f48fa64 commit fc72b77
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 15 deletions.
24 changes: 16 additions & 8 deletions src/cmd/compile/internal/noder/reader.go
Expand Up @@ -154,6 +154,8 @@ type readerDict struct {
funcsObj []ir.Node

itabs []itabInfo2

methodExprs []ir.Node
}

type itabInfo2 struct {
Expand Down Expand Up @@ -776,6 +778,14 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, ex
dict.itabs[i] = itabInfo2{typ: typ, lsym: lsym}
}

dict.methodExprs = make([]ir.Node, r.Len())
for i := range dict.methodExprs {
recv := pr.typIdx(typeInfo{idx: pkgbits.Index(r.Len()), derived: true}, &dict, true)
_, sym := r.selector()

dict.methodExprs[i] = typecheck.Expr(ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, ir.TypeNode(recv), sym))
}

return &dict
}

Expand Down Expand Up @@ -1696,15 +1706,13 @@ func (r *reader) expr() (res ir.Node) {
case exprSelector:
var x ir.Node
if r.Bool() { // MethodExpr
x = r.exprType(false)

// Method expression with derived receiver type.
if x.Op() == ir.ODYNAMICTYPE {
// TODO(mdempsky): Handle with runtime dictionary lookup.
n := ir.TypeNode(x.Type())
n.SetTypecheck(1)
x = n
if r.Bool() {
return r.dict.methodExprs[r.Len()]
}

n := ir.TypeNode(r.typ())
n.SetTypecheck(1)
x = n
} else { // FieldVal, MethodVal
x = r.expr()
}
Expand Down
76 changes: 70 additions & 6 deletions src/cmd/compile/internal/noder/writer.go
Expand Up @@ -177,6 +177,10 @@ type writerDict struct {
// itabs lists itabs that are needed for dynamic type assertions
// (including type switches).
itabs []itabInfo

// methodsExprs lists method expressions with derived-type receiver
// parameters.
methodExprs []methodExprInfo
}

// A derivedInfo represents a reference to an encoded generic Go type.
Expand Down Expand Up @@ -208,13 +212,26 @@ type objInfo struct {
// An itabInfo represents a reference to an encoded itab entry (i.e.,
// a non-empty interface type along with a concrete type that
// implements that interface).
//
// itabInfo is only used for
type itabInfo struct {
typIdx pkgbits.Index // always a derived type index
iface typeInfo // always a non-empty interface type
}

// A methodExprInfo represents a reference to an encoded method
// expression, whose receiver parameter is a derived type.
type methodExprInfo struct {
recvIdx pkgbits.Index // always a derived type index
methodInfo selectorInfo
}

// A selectorInfo represents a reference to an encoded field or method
// name (i.e., objects that can only be accessed using selector
// expressions).
type selectorInfo struct {
pkgIdx pkgbits.Index
nameIdx pkgbits.Index
}

// anyDerived reports whether any of info's explicit type arguments
// are derived types.
func (info objInfo) anyDerived() bool {
Expand Down Expand Up @@ -296,8 +313,12 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index {

// pkg writes a use of the given Package into the element bitstream.
func (w *writer) pkg(pkg *types2.Package) {
w.pkgRef(w.p.pkgIdx(pkg))
}

func (w *writer) pkgRef(idx pkgbits.Index) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg))
w.Reloc(pkgbits.RelocPkg, idx)
}

// pkgIdx returns the index for the given package, adding it to the
Expand Down Expand Up @@ -793,6 +814,12 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) {
w.typInfo(itab.iface)
}

w.Len(len(dict.methodExprs))
for _, methodExpr := range dict.methodExprs {
w.Len(int(methodExpr.recvIdx))
w.selectorInfo(methodExpr.methodInfo)
}

assert(len(dict.derived) == nderived)
assert(len(dict.funcs) == nfuncs)
}
Expand Down Expand Up @@ -862,9 +889,19 @@ func (w *writer) localIdent(obj types2.Object) {
// selector writes the name of a field or method (i.e., objects that
// can only be accessed using selector expressions).
func (w *writer) selector(obj types2.Object) {
w.selectorInfo(w.p.selectorIdx(obj))
}

func (w *writer) selectorInfo(info selectorInfo) {
w.Sync(pkgbits.SyncSelector)
w.pkg(obj.Pkg())
w.String(obj.Name())
w.pkgRef(info.pkgIdx)
w.StringRef(info.nameIdx)
}

func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo {
pkgIdx := pw.pkgIdx(obj.Pkg())
nameIdx := pw.StringIdx(obj.Name())
return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx}
}

// @@@ Compiler extensions
Expand Down Expand Up @@ -1490,7 +1527,19 @@ func (w *writer) expr(expr syntax.Expr) {

w.Code(exprSelector)
if w.Bool(sel.Kind() == types2.MethodExpr) {
w.exprType(nil, expr.X, false)
tv, ok := w.p.info.Types[expr.X]
assert(ok)
assert(tv.IsType())

typInfo := w.p.typIdx(tv.Type, w.dict)
if w.Bool(typInfo.derived) {
methodInfo := w.p.selectorIdx(sel.Obj())
idx := w.dict.methodExprIdx(typInfo, methodInfo)
w.Len(idx)
break
}

w.typInfo(typInfo)
} else {
w.expr(expr.X)
}
Expand Down Expand Up @@ -1821,6 +1870,21 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) {
w.typInfo(info)
}

func (dict *writerDict) methodExprIdx(recvInfo typeInfo, methodInfo selectorInfo) int {
assert(recvInfo.derived)
newInfo := methodExprInfo{recvIdx: recvInfo.idx, methodInfo: methodInfo}

for idx, oldInfo := range dict.methodExprs {
if oldInfo == newInfo {
return idx
}
}

idx := len(dict.methodExprs)
dict.methodExprs = append(dict.methodExprs, newInfo)
return idx
}

// isInterface reports whether typ is known to be an interface type.
// If typ is a type parameter, then isInterface reports an internal
// compiler error instead.
Expand Down
8 changes: 7 additions & 1 deletion src/internal/pkgbits/encoder.go
Expand Up @@ -316,8 +316,14 @@ func (w *Encoder) Code(c Code) {
// section (if not already present), and then writing a relocation
// into the element bitstream.
func (w *Encoder) String(s string) {
w.StringRef(w.p.StringIdx(s))
}

// StringRef writes a reference to the given index, which must be a
// previously encoded string value.
func (w *Encoder) StringRef(idx Index) {
w.Sync(SyncString)
w.Reloc(RelocString, w.p.StringIdx(s))
w.Reloc(RelocString, idx)
}

// Strings encodes and writes a variable-length slice of strings into
Expand Down

0 comments on commit fc72b77

Please sign in to comment.