package solve

import (
	
	
	

	
	
	
)

// tryJoint instantiates result-generic templates from the demands that are still
// unserved. The pending demands are clustered into one binding per distinct
// pinning, and the template is instantiated once per cluster, lifting the
// parameters no demand pins, so one template may serve several consumers at
// different types (the multi-result analog of pinning a single-result template at
// several types).
//
// This is the general path; the worklist is the fast case ahead of it. The
// worklist first handles every demand that a single result pins completely.
// Whatever it cannot fully pin from one demand (a demand that matches only a
// partial result while another result mentions more parameters, or pinnings that
// arrive jointly across disjoint results) falls to clustering here, where the
// unpinned parameters are lifted. Dispatch is therefore driven by what the
// demands actually pin, not by whether some result structurally covers every
// parameter: a template whose all-covering result is never the one demanded is
// still served, by lifting the parameters its demanded (partial) result leaves free.
//
// Two clusters never produce the same output type, because every distinct pin for
// a parameter lands in exactly one cluster, and clusters seed only from still-
// unsatisfied demands, so a type an existing instance already supplies never
// drives a second instantiation; a genuine duplicate across providers still
// surfaces downstream as an ambiguous producer.
func ( *solver) () (bool, *diag.Error) {
	if  := .checkTemplateAmbiguity();  != nil {
		return false, 
	}
	for ,  := range .resGen {
		 := false
		for ,  := range .jointClusters() {
			if .jointClusterDone(, ) {
				continue
			}
			.markJointClusterDone(, )
			,  := .instantiateTemplate(, )
			if  != nil {
				return false, 
			}
			if  {
				 = true
			}
		}
		if  {
			return true, nil
		}
	}
	return false, nil
}

// jointClusters groups the still-unsatisfied demands that pin p's results into
// consistent bindings: one per distinct pinning. Each demand contributes the
// binding it forces on p's result parameters; demands whose bindings agree merge
// into one cluster, and conflicting ones form separate clusters, each a distinct
// instantiation. Clusters are built in deterministic demand order, and a
// parameter no demand pins is left for instantiateTemplate to lift. Seeding from
// the unsatisfied demands (not every demand) keeps a type an existing instance
// already supplies from driving a redundant (and possibly conflicting)
// instantiation as later demands arrive across fixpoint passes.
func ( *solver) ( *discover.Provider) []map[*types.TypeParam]types.Type {
	 := paramSet()
	 := .skeleton()
	 := .valueOuts()

	var  []map[*types.TypeParam]types.Type
	for ,  := range .pendingDemands() {
		,  := clusterCands(, , )
		if ! {
			// None of the results unifies with d's exact form: try the dual, so a
			// pointer demand can pin a template producing the value form: the
			// instantiation supplies the dual and d rides the bridge.
			if ,  := gotypes.DualType();  {
				, _ = clusterCands(, , )
			}
		}
		if len() == 0 {
			continue
		}

		// Prefer merging one candidate into an existing cluster, filling a slot that
		// cluster leaves open. This is joint pinning: different demands pinning
		// different slots of one instance. Validate that the merged binding actually
		// instantiates, so a near-miss pin falls into its own cluster to lift around
		// rather than poisoning this one (compatibleBind alone would merge a disjoint
		// pin whose near-miss then drops the whole cluster, stranding the valid pin).
		 := false
		for ,  := range  {
			for ,  := range  {
				if !compatibleBind(, ) {
					continue
				}
				 := maps.Clone()
				maps.Copy(, )
				if !.bindInstantiates(, ) {
					continue
				}
				maps.Copy(, )
				 = true
				break
			}
			if  {
				break
			}
		}
		if  {
			continue
		}

		// New cluster: seed it from the first candidate that instantiates cleanly,
		// falling back to the first slot so a demand whose every pin is a near-miss
		// still forms a cluster that instantiateTemplate records as such, leaving the
		// demand to become an injector input.
		 := [0]
		for ,  := range  {
			if .bindInstantiates(, ) {
				 = 
				break
			}
		}
		 = append(, )
	}
	return 
}

// clusterCands collects one candidate binding per value result the demand
// unifies with, in result order, counting only bindings that pin at least one
// parameter. A demand pins at most one result slot: merging its per-slot
// bindings into one would, when two slots share an outer generic type (A[T] and
// A[U] both matched by A[int]), pin both parameters from a single demand and
// make the template produce that type twice, a spurious self-collision. matched
// reports whether any result unified at all: a parameter-free result yields no
// binding but still means the exact form matched, so the caller must not fall
// through to the dual.
func ( []types.Type,  types.Type,  map[*types.TypeParam]bool) ( []map[*types.TypeParam]types.Type,  bool) {
	for ,  := range  {
		 := map[*types.TypeParam]types.Type{}
		if gotypes.Unify(, , , ) {
			 = true
			if len() > 0 {
				 = append(, )
			}
		}
	}
	return , 
}

// jointClusterDone reports whether p has already been instantiated for a cluster
// with these pinned bindings. A cluster is identified by its pinned bindings
// alone: parameters it leaves to be freshly lifted would otherwise vary the
// instance identity every pass, so the unpinned slots are filled with a
// sentinel that is identical only to itself.
func ( *solver) ( *discover.Provider,  map[*types.TypeParam]types.Type) bool {
	 := .jointDone[]
	return  != nil && .Contains(jointBindKey(, ))
}

func ( *solver) ( *discover.Provider,  map[*types.TypeParam]types.Type) {
	 := .jointDone[]
	if  == nil {
		 = new(gotypes.Set[*types.Tuple])
		.jointDone[] = 
	}
	.Add(jointBindKey(, ))
}

// jointUnpinned is a unique sentinel type standing for a cluster parameter no
// demand pinned. Being a fresh named type, it is identical only to itself, so it
// never coincides with a real pinned type.
var jointUnpinned types.Type = types.NewNamed(
	types.NewTypeName(token.NoPos, nil, "unpinned", nil),
	types.NewStruct(nil, nil), nil)

func ( *discover.Provider,  map[*types.TypeParam]types.Type) *types.Tuple {
	 := make([]types.Type, .Tparams.Len())
	for  := range  {
		if ,  := [.Tparams.At()];  {
			[] = 
		} else {
			[] = jointUnpinned
		}
	}
	return gotypes.ListKey()
}