1// Copyright 2021 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package types2 6 7import ( 8 "bytes" 9 "fmt" 10 "strconv" 11 "strings" 12 "sync" 13) 14 15// An Context is an opaque type checking context. It may be used to share 16// identical type instances across type-checked packages or calls to 17// Instantiate. 18// 19// It is safe for concurrent use. 20type Context struct { 21 mu sync.Mutex 22 typeMap map[string][]ctxtEntry // type hash -> instances entries 23 nextID int // next unique ID 24 originIDs map[Type]int // origin type -> unique ID 25} 26 27type ctxtEntry struct { 28 orig Type 29 targs []Type 30 instance Type // = orig[targs] 31} 32 33// NewContext creates a new Context. 34func NewContext() *Context { 35 return &Context{ 36 typeMap: make(map[string][]ctxtEntry), 37 originIDs: make(map[Type]int), 38 } 39} 40 41// instanceHash returns a string representation of typ instantiated with targs. 42// The hash should be a perfect hash, though out of caution the type checker 43// does not assume this. The result is guaranteed to not contain blanks. 44func (ctxt *Context) instanceHash(orig Type, targs []Type) string { 45 assert(ctxt != nil) 46 assert(orig != nil) 47 var buf bytes.Buffer 48 49 h := newTypeHasher(&buf, ctxt) 50 h.string(strconv.Itoa(ctxt.getID(orig))) 51 // Because we've already written the unique origin ID this call to h.typ is 52 // unnecessary, but we leave it for hash readability. It can be removed later 53 // if performance is an issue. 54 h.typ(orig) 55 if len(targs) > 0 { 56 // TODO(rfindley): consider asserting on isGeneric(typ) here, if and when 57 // isGeneric handles *Signature types. 58 h.typeList(targs) 59 } 60 61 return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 62} 63 64// lookup returns an existing instantiation of orig with targs, if it exists. 65// Otherwise, it returns nil. 66func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type { 67 ctxt.mu.Lock() 68 defer ctxt.mu.Unlock() 69 70 for _, e := range ctxt.typeMap[h] { 71 if identicalInstance(orig, targs, e.orig, e.targs) { 72 return e.instance 73 } 74 if debug { 75 // Panic during development to surface any imperfections in our hash. 76 panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance)) 77 } 78 } 79 80 return nil 81} 82 83// update de-duplicates n against previously seen types with the hash h. If an 84// identical type is found with the type hash h, the previously seen type is 85// returned. Otherwise, n is returned, and recorded in the Context for the hash 86// h. 87func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type { 88 assert(inst != nil) 89 90 ctxt.mu.Lock() 91 defer ctxt.mu.Unlock() 92 93 for _, e := range ctxt.typeMap[h] { 94 if inst == nil || Identical(inst, e.instance) { 95 return e.instance 96 } 97 if debug { 98 // Panic during development to surface any imperfections in our hash. 99 panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance)) 100 } 101 } 102 103 ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{ 104 orig: orig, 105 targs: targs, 106 instance: inst, 107 }) 108 109 return inst 110} 111 112// getID returns a unique ID for the type t. 113func (ctxt *Context) getID(t Type) int { 114 ctxt.mu.Lock() 115 defer ctxt.mu.Unlock() 116 id, ok := ctxt.originIDs[t] 117 if !ok { 118 id = ctxt.nextID 119 ctxt.originIDs[t] = id 120 ctxt.nextID++ 121 } 122 return id 123} 124