// 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 (
	
	
	
	
	
)

// 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][]ctxtEntry // type hash -> instances entries
	nextID    int                    // next unique ID
	originIDs map[Type]int           // origin type -> unique ID
}

type ctxtEntry struct {
	orig     Type
	targs    []Type
	instance Type // = orig[targs]
}

// NewContext creates a new Context.
func () *Context {
	return &Context{
		typeMap:   make(map[string][]ctxtEntry),
		originIDs: make(map[Type]int),
	}
}

// instanceHash returns a string representation of typ instantiated with targs.
// The hash should be a perfect hash, though out of caution the type checker
// does not assume this. The result is guaranteed to not contain blanks.
func ( *Context) ( Type,  []Type) string {
	assert( != nil)
	assert( != nil)
	var  bytes.Buffer

	 := newTypeHasher(&, )
	.string(strconv.Itoa(.getID()))
	// Because we've already written the unique origin ID this call to h.typ is
	// unnecessary, but we leave it for hash readability. It can be removed later
	// if performance is an issue.
	.typ()
	if len() > 0 {
		// TODO(rfindley): consider asserting on isGeneric(typ) here, if and when
		// isGeneric handles *Signature types.
		.typeList()
	}

	return strings.Replace(.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4
}

// lookup returns an existing instantiation of orig with targs, if it exists.
// Otherwise, it returns nil.
func ( *Context) ( string,  Type,  []Type) Type {
	.mu.Lock()
	defer .mu.Unlock()

	for ,  := range .typeMap[] {
		if identicalInstance(, , .orig, .targs) {
			return .instance
		}
		if debug {
			// Panic during development to surface any imperfections in our hash.
			panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", , , .instance))
		}
	}

	return nil
}

// update de-duplicates n against previously seen types with the hash h.  If an
// identical type is found with the type hash h, the previously seen type is
// returned. Otherwise, n is returned, and recorded in the Context for the hash
// h.
func ( *Context) ( string,  Type,  []Type,  Type) Type {
	assert( != nil)

	.mu.Lock()
	defer .mu.Unlock()

	for ,  := range .typeMap[] {
		if  == nil || Identical(, .instance) {
			return .instance
		}
		if debug {
			// Panic during development to surface any imperfections in our hash.
			panic(fmt.Sprintf("%s and %s are not identical", , .instance))
		}
	}

	.typeMap[] = append(.typeMap[], ctxtEntry{
		orig:     ,
		targs:    ,
		instance: ,
	})

	return 
}

// getID returns a unique ID for the type t.
func ( *Context) ( Type) int {
	.mu.Lock()
	defer .mu.Unlock()
	,  := .originIDs[]
	if ! {
		 = .nextID
		.originIDs[] = 
		.nextID++
	}
	return 
}