// Copyright 2026 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

// This file defines a hash function for Types.

import (
	
	
)

type (
	// Hasher defines a hash function and equivalence relation
	// for [Types] that is consistent with [Identical].
	// Hashers are stateless.
	Hasher struct{}

	// HasherIgnoreTags is a variant of [Hasher] that is
	// consistent with [IdenticalIgnoreTags].
	HasherIgnoreTags struct{}
)

var (
	_ maphash.Hasher[Type] = Hasher{}
	_ maphash.Hasher[Type] = HasherIgnoreTags{}
)

func (Hasher) ( *maphash.Hash,  Type) {
	// The two hashers use essentially the same hash function,
	// which ignores tags; only the Equal methods vary.
	// But for future-proofing we gratuitously force them
	// to differ by one byte.
	.WriteByte(0)
	hasher{inGenericSig: false}.hash(, )
}
func (HasherIgnoreTags) ( *maphash.Hash,  Type) {
	.WriteByte(1)
	hasher{inGenericSig: false}.hash(, )
}

func (Hasher) (,  Type) bool           { return Identical(, ) }
func (HasherIgnoreTags) (,  Type) bool { return IdenticalIgnoreTags(, ) }

// hasher holds the state of a single hash traversal, namely,
// whether we are inside the signature of a generic function.
// This is used to optimize [hasher.hashTypeParam].
type hasher struct{ inGenericSig bool }

func ( hasher) ( *maphash.Hash,  Type) {
	// See [Identical] for rationale.
	switch t := .(type) {
	case *Alias:
		.(, Unalias())

	case *Array:
		.WriteByte('A')
		maphash.WriteComparable(, .Len())
		.(, .Elem())

	case *Basic:
		.WriteByte('B')
		.WriteByte(byte(.Kind()))

	case *Chan:
		.WriteByte('C')
		.WriteByte(byte(.Dir()))
		.(, .Elem())

	case *Interface:
		.WriteByte('I')
		.WriteByte(byte(.NumMethods()))

		// Interfaces are identical if they have the same set of methods, with
		// identical names and types, and they have the same set of type
		// restrictions. See [Identical] for more details.

		// Hash the methods.
		//
		// Because [Identical] treats Methods as an unordered set,
		// we must either:
		// (a) sort the methods into some canonical order; or
		// (b) hash them each in parallel, combine them with a
		//     commutative operation such as + or ^, and then
		//     write this value into the primary hasher.
		// Since (a) requires allocation, we choose (b).
		var  uint64
		for  := range .Methods() {
			var  maphash.Hash
			.SetSeed(.Seed())
			// Ignore m.Pkg().
			// Use shallow hash on method signature to
			// avoid anonymous interface cycles.
			.WriteString(.Name())
			.shallowHash(&, .Type())
			 ^= .Sum64()
		}
		maphash.WriteComparable(, )

		// Hash type restrictions.
		// TODO(adonovan): call (fork of) InterfaceTermSet from
		// golang.org/x/tools/internal/typeparams/normalize.go.
		// hr.hashTermSet(h, terms)

	case *Map:
		.WriteByte('M')
		.(, .Key())
		.(, .Elem())

	case *Named:
		.WriteByte('N')
		.hashTypeName(, .Obj())
		for  := range .TypeArgs().Types() {
			.(, )
		}

	case *Pointer:
		.WriteByte('P')
		.(, .Elem())

	case *Signature:
		.WriteByte('F')
		maphash.WriteComparable(, .Variadic())
		 := .TypeParams()
		if  := .Len();  > 0 {
			.inGenericSig = true // affects constraints, params, and results

			maphash.WriteComparable(, )
			for  := range .TypeParams() {
				.(, .Constraint())
			}
		}
		.hashTuple(, .Params())
		.hashTuple(, .Results())

	case *Slice:
		.WriteByte('S')
		.(, .Elem())

	case *Struct:
		.WriteByte('R') // mnemonic: a struct is a record type
		 := .NumFields()
		.WriteByte(byte())
		for  := range  {
			 := .Field()
			maphash.WriteComparable(, .Anonymous())
			// Ignore t.Tag(i), so that a single hash function
			// can be used with both [Identical] and [IdenticalIgnoreTags].
			.WriteString(.Name()) // (ignore f.Pkg)
			.(, .Type())
		}

	case *Tuple:
		.hashTuple(, )

	case *TypeParam:
		.hashTypeParam(, )

	case *Union:
		.WriteByte('U')
		// TODO(adonovan): opt: call (fork of) UnionTermSet from
		// golang.org/x/tools/internal/typeparams/normalize.go.
		// hr.hashTermSet(h, terms)

	default:
		panic(fmt.Sprintf("%T: %v", , ))
	}
}

func ( hasher) ( *maphash.Hash,  *Tuple) {
	.WriteByte('T')
	.WriteByte(byte(.Len()))
	for  := range .Variables() {
		.hash(, .Type())
	}
}

// func (hr hasher) hashTermSet(h *maphash.Hash, terms []*Term) {
// 	h.WriteByte(byte(len(terms)))
// 	for _, term := range terms {
// 		// term order is not significant.
// 		h.WriteByte(byte(btoi(term.Tilde())))
// 		hr.hash(h, term.Type())
// 	}
// }

// hashTypeParam encodes a type parameter into hasher h.
func ( hasher) ( *maphash.Hash,  *TypeParam) {
	.WriteByte('P')
	// Within the signature of a generic function, TypeParams are
	// identical if they have the same index and constraint, so we
	// hash them based on index.
	//
	// When we are outside a generic function, free TypeParams are
	// identical iff they are the same object, so we can use a
	// more discriminating hash consistent with object identity.
	// This optimization saves [Map] about 4% when hashing all the
	// Info.Types in the forward closure of net/http.
	if !.inGenericSig {
		// Optimization: outside a generic function signature,
		// use a more discrimating hash consistent with object identity.
		.hashTypeName(, .Obj())
	} else {
		.WriteByte(byte(.Index()))
	}
}

// hashTypeName hashes the pointer of tname.
func (hasher) ( *maphash.Hash,  *TypeName) {
	.WriteByte('N')
	// Since Identical uses == to compare TypeNames,
	// the hash function uses maphash.Comparable.
	maphash.WriteComparable(, )
}

// shallowHash computes a hash of t without looking at any of its
// element Types, to avoid potential anonymous cycles in the types of
// interface methods.
//
// When an unnamed non-empty interface type appears anywhere among the
// arguments or results of an interface method, there is a potential
// for endless recursion. Consider:
//
//	type X interface { m() []*interface { X } }
//
// The problem is that the Methods of the interface in m's result type
// include m itself; there is no mention of the named type X that
// might help us break the cycle.
// (See comment in [Identical], case *Interface, for more.)
func ( hasher) ( *maphash.Hash,  Type) {
	// t is the type of an interface method (Signature),
	// its params or results (Tuples), or their immediate
	// elements (mostly Slice, Pointer, Basic, Named),
	// so there's no need to optimize anything else.
	switch t := .(type) {
	case *Alias:
		.(, Unalias())

	case *Array:
		.WriteByte('A')
		maphash.WriteComparable(, .Len())
		// ignore t.Elem()

	case *Basic:
		.WriteByte('B')
		.WriteByte(byte(.Kind()))

	case *Chan:
		.WriteByte('C')
		// ignore Dir(), Elem()

	case *Interface:
		.WriteByte('I')
		// no recursion here

	case *Map:
		.WriteByte('M')
		// ignore Key(), Elem()

	case *Named:
		.hashTypeName(, .Obj())

	case *Pointer:
		.WriteByte('P')
		// ignore t.Elem()

	case *Signature:
		.WriteByte(byte(btoi(.Variadic())))
		// The Signature/Tuple recursion is always
		// finite and invariably shallow.
		.(, .Params())
		.(, .Results())

	case *Slice:
		.WriteByte('S')
		// ignore t.Elem()

	case *Struct:
		.WriteByte('R') // mnemonic: a struct is a record type
		.WriteByte(byte(.NumFields()))
		// ignore t.Fields()

	case *Tuple:
		.WriteByte('T')
		.WriteByte(byte(.Len()))
		for  := range .Variables() {
			.(, .Type())
		}

	case *TypeParam:
		.hashTypeParam(, )

	case *Union:
		.WriteByte('U')
		// ignore term set

	default:
		panic(fmt.Sprintf("shallowHash: %T: %v", , ))
	}
}

func ( bool) int {
	if  {
		return 1
	} else {
		return 0
	}
}