blob: 99baad8d0f6b95a86750c46e2252c7339f8923f4 [file] [log] [blame]
// 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 types
import (
"bytes"
"strings"
"sync"
)
// An Context is an opaque type checking context. It may be used to share
// identical type instances across type-checked packages or calls to
// Instantiate.
//
// It is safe for concurrent use.
type Context struct {
mu sync.Mutex
typeMap map[string]*Named // type hash -> instance
nextID int // next unique ID
seen map[*Named]int // assigned unique IDs
}
// NewContext creates a new Context.
func NewContext() *Context {
return &Context{
typeMap: make(map[string]*Named),
seen: make(map[*Named]int),
}
}
// typeHash returns a string representation of typ, which can be used as an exact
// type hash: types that are identical produce identical string representations.
// If typ is a *Named type and targs is not empty, typ is printed as if it were
// instantiated with targs. The result is guaranteed to not contain blanks (" ").
func (ctxt *Context) typeHash(typ Type, targs []Type) string {
assert(ctxt != nil)
assert(typ != nil)
var buf bytes.Buffer
h := newTypeHasher(&buf, ctxt)
if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
// Don't use WriteType because we need to use the provided targs
// and not any targs that might already be with the *Named type.
h.typePrefix(named)
h.typeName(named.obj)
h.typeList(targs)
} else {
assert(targs == nil)
h.typ(typ)
}
return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4
}
// typeForHash returns the recorded type for the type hash h, if it exists.
// If no type exists for h and n is non-nil, n is recorded for h.
func (ctxt *Context) typeForHash(h string, n *Named) *Named {
ctxt.mu.Lock()
defer ctxt.mu.Unlock()
if existing := ctxt.typeMap[h]; existing != nil {
return existing
}
if n != nil {
ctxt.typeMap[h] = n
}
return n
}
// idForType returns a unique ID for the pointer n.
func (ctxt *Context) idForType(n *Named) int {
ctxt.mu.Lock()
defer ctxt.mu.Unlock()
id, ok := ctxt.seen[n]
if !ok {
id = ctxt.nextID
ctxt.seen[n] = id
ctxt.nextID++
}
return id
}