package gotypes

import (
	
	
)

// Subst returns t with every type parameter in m replaced by its mapped type.
// Only the nodes on the path to a substituted parameter are rebuilt; a subtree
// that mentions none of the mapped parameters is returned unchanged, so special
// types such as the predeclared comparable constraint and named cross-package
// interfaces keep their identity.
//
// ctxt deduplicates any generic types Subst must re-instantiate; pass the caller's
// shared context so identical instantiations resolve to one instance. A nil ctxt
// is valid (a throwaway): correctness never depends on instance identity, because
// the result is rendered to source text and inspected structurally (go/types
// Identical, underlying-type assertions, reachability walks), never keyed by an
// instance pointer.
func ( *types.Context,  types.Type,  map[*types.TypeParam]types.Type) types.Type {
	return (&subster{ctxt: , m: }).subst()
}

type subster struct {
	ctxt *types.Context
	m    map[*types.TypeParam]types.Type
}

func ( *subster) ( types.Type) types.Type {
	if  == nil || !.mentions() {
		return 
	}
	// Past the guard the type mentions a substituted parameter, so it must be
	// rebuilt; collapse any alias to its target first (the alias name cannot
	// survive substitution of what it expands to).
	 = types.Unalias()
	switch u := .(type) {
	case *types.TypeParam:
		if ,  := .m[];  {
			return 
		}
		return 
	case *types.Pointer:
		return types.NewPointer(.(.Elem()))
	case *types.Slice:
		return types.NewSlice(.(.Elem()))
	case *types.Array:
		return types.NewArray(.(.Elem()), .Len())
	case *types.Chan:
		return types.NewChan(.Dir(), .(.Elem()))
	case *types.Map:
		return types.NewMap(.(.Key()), .(.Elem()))
	case *types.Union:
		 := make([]*types.Term, .Len())
		for  := range .Len() {
			 := .Term()
			[] = types.NewTerm(.Tilde(), .(.Type()))
		}
		return types.NewUnion()
	case *types.Interface:
		var  []*types.Func
		for  := range .ExplicitMethods() {
			 := .(.Type()).(*types.Signature)
			 = append(, types.NewFunc(.Pos(), .Pkg(), .Name(), ))
		}
		var  []types.Type
		for  := range .EmbeddedTypes() {
			 = append(, .())
		}
		 := types.NewInterfaceType(, )
		.Complete()
		return 
	case *types.Signature:
		return types.NewSignatureType(nil, nil, nil,
			.substTuple(.Params()), .substTuple(.Results()), .Variadic())
	case *types.Struct:
		 := .NumFields()
		 := make([]*types.Var, )
		 := make([]string, )
		for  := range  {
			 := .Field()
			[] = types.NewField(.Pos(), .Pkg(), .Name(), .(.Type()), .Embedded())
			[] = .Tag()
		}
		return types.NewStruct(, )
	case *types.Named:
		 := .TypeArgs()
		if .Len() == 0 { // TypeList.Len is nil-safe
			return 
		}
		 := make([]types.Type, .Len())
		for  := range .Len() {
			[] = .(.At())
		}
		// validate=false: with a generic origin and the right number of type
		// arguments (both guaranteed here: u is an instantiated Named and na has one
		// entry per original arg), Instantiate cannot return an error. A non-nil error
		// would mean that invariant broke; returning the un-substituted type would
		// leak the template's own parameters into a lifted constraint, so fail loud.
		,  := types.Instantiate(.ctxt, .Origin(), , false)
		if  != nil {
			panic(fmt.Sprintf("plumb: Subst: re-instantiating %s failed: %v", , ))
		}
		return 
	default:
		// Unreachable: past the mentions guard t references a type parameter, so
		// it cannot be a kind (Basic, Tuple) that mentions none.
		panic(fmt.Sprintf("plumb: subst on unsupported type %T", ))
	}
}

func ( *subster) ( *types.Tuple) *types.Tuple {
	 := make([]*types.Var, .Len())
	for  := range .Len() {
		 := .At()
		[] = types.NewVar(.Pos(), .Pkg(), .Name(), .subst(.Type()))
	}
	return types.NewTuple(...)
}

// mentions reports whether t references any type parameter in the substitution
// map s.m. It shares typeContains's exhaustive type walk, testing membership in
// s.m at each type-parameter leaf rather than an explicit params set.
func ( *subster) ( types.Type) bool {
	return typeContains(, &Set[types.Type]{}, func( types.Type) bool {
		,  := .(*types.TypeParam)
		if ! {
			return false
		}
		_,  = .m[]
		return 
	})
}