go/ssa: Put type canonicalization on own mutex.

Put Program level type canonicalization on its own Mutex. With generics, canonical types are requested whenever instantiating a type. This simplifies when canonical types can be requested at the cost of maintaining two typeutil.Hashers.

Updates golang/go#48525

Change-Id: I2376a43e43f410d10a6d87158816e728aba746ca
Reviewed-on: https://go-review.googlesource.com/c/tools/+/386315
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Trust: Tim King <taking@google.com>
This commit is contained in:
Tim King 2022-02-16 12:39:07 -08:00
parent afc5fce285
commit 6a6eb596e7
4 changed files with 50 additions and 11 deletions

View File

@ -34,7 +34,6 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
h := typeutil.MakeHasher() // protected by methodsMu, in effect
prog.methodSets.SetHasher(h)
prog.canon.SetHasher(h)
return prog
}

View File

@ -26,10 +26,11 @@ type Program struct {
mode BuilderMode // set of mode bits for SSA construction
MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets
canon canonizer // type canonicalization map
methodsMu sync.Mutex // guards the following maps:
methodSets typeutil.Map // maps type to its concrete methodSet
runtimeTypes typeutil.Map // types for which rtypes are needed
canon typeutil.Map // type canonicalization map
bounds map[*types.Func]*Function // bounds for curried x.Method closures
thunks map[selectionKey]*Function // thunks for T.Method expressions
}

View File

@ -13,10 +13,22 @@ import (
"go/types"
"io"
"os"
"sync"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types/typeutil"
)
//// Sanity checking utilities
// assert panics with the mesage msg if p is false.
// Avoid combining with expensive string formatting.
func assert(p bool, msg string) {
if !p {
panic(msg)
}
}
//// AST utilities
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
@ -87,3 +99,36 @@ func makeLen(T types.Type) *Builtin {
sig: types.NewSignature(nil, lenParams, lenResults, false),
}
}
// Mapping of a type T to a canonical instance C s.t. types.Indentical(T, C).
// Thread-safe.
type canonizer struct {
mu sync.Mutex
canon typeutil.Map // map from type to a canonical instance
}
// Tuple returns a canonical representative of a Tuple of types.
// Representative of the empty Tuple is nil.
func (c *canonizer) Tuple(ts []types.Type) *types.Tuple {
if len(ts) == 0 {
return nil
}
vars := make([]*types.Var, len(ts))
for i, t := range ts {
vars[i] = anonVar(t)
}
tuple := types.NewTuple(vars...)
return c.Type(tuple).(*types.Tuple)
}
// Type returns a canonical representative of type T.
func (c *canonizer) Type(T types.Type) types.Type {
c.mu.Lock()
defer c.mu.Unlock()
if r := c.canon.At(T); r != nil {
return r.(types.Type)
}
c.canon.Set(T, T)
return T
}

View File

@ -246,9 +246,11 @@ func makeThunk(prog *Program, sel *types.Selection) *Function {
panic(sel)
}
// Canonicalize sel.Recv() to avoid constructing duplicate thunks.
canonRecv := prog.canon.Type(sel.Recv())
key := selectionKey{
kind: sel.Kind(),
recv: sel.Recv(),
recv: canonRecv,
obj: sel.Obj(),
index: fmt.Sprint(sel.Index()),
indirect: sel.Indirect(),
@ -257,14 +259,6 @@ func makeThunk(prog *Program, sel *types.Selection) *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
// Canonicalize key.recv to avoid constructing duplicate thunks.
canonRecv, ok := prog.canon.At(key.recv).(types.Type)
if !ok {
canonRecv = key.recv
prog.canon.Set(key.recv, canonRecv)
}
key.recv = canonRecv
fn, ok := prog.thunks[key]
if !ok {
fn = makeWrapper(prog, sel)