package solve

import (
	
	
	

	
	
	
)

// This file holds the pinning-revision machinery layered on top of the base
// demand fixpoint: the restart signal and bound, and the solver methods that
// decide when to commit a merged pinning and replay. Its persist-across-restart
// state (the commitments, refusedUnions, and pinnings fields) lives on the
// solver in solve.go; the replay loop that consumes errRestart is in Set.

// maxRestarts bounds pinning-revision restarts. Each restart commits a pinning
// strictly larger than an existing instance's (or is preempted by a one-shot
// refusal), so the bound is far above any real program.
const maxRestarts = 100

// errRestart signals, up through the fixpoint to Set, that a new pinning was
// committed and resolution must replay from seed with it in force. It rides the
// *diag.Error return channel but is a control-flow signal, not a diagnostic:
// every layer compares it by identity (err == errRestart) and Set consumes it
// before it can escape. It is a diag.Sentinel rather than a zero-value *Error so
// that a stray format of an in-flight error prints the message instead of
// nil-panicking.
var errRestart = diag.Sentinel("plumb: internal: pinning-revision restart")

// applyCommitment merges bind with the first committed pinning of p it is
// compatible with, so a replay instantiates the union where the aborted run
// would have split. A binding incompatible with every commitment (a distinct
// instantiation of the same template) passes through unchanged.
func ( *solver) ( *discover.Provider,  map[*types.TypeParam]types.Type) map[*types.TypeParam]types.Type {
	for ,  := range .commitments[] {
		if compatibleBind(, ) {
			 := maps.Clone()
			maps.Copy(, )
			return 
		}
	}
	return 
}

// reviseOrSplit is the pinning-revision trigger: about to instantiate p at
// bind, it looks for an existing instance of p whose pinning is compatible
// with bind and would gain pins from it. When their union is viable and
// mentions no lifted parameter, the union becomes a committed pinning and the
// solve restarts to replay with it in force; otherwise the union is refused
// once (recorded so it never re-triggers) and the split stands, the same
// result the resolver reaches when no revision applies. Pinnings that conflict
// on a shared slot are ordinary multi-instantiation and never trigger.
func ( *solver) ( *discover.Provider,  map[*types.TypeParam]types.Type) *diag.Error {
	for ,  := range .instances {
		if .Prov !=  {
			continue
		}
		 := .pinnings[]
		if  == nil || !compatibleBind(, ) {
			continue
		}
		 := maps.Clone()
		maps.Copy(, )
		if len() == len() {
			continue // no new pins; instance dedup covers a subsumed binding
		}
		if .unionRefused(, ) {
			continue
		}
		 := slices.ContainsFunc(slices.Collect(maps.Values()), gotypes.ContainsTypeParam)
		if  || !.bindInstantiates(, ) {
			// A lifted-parameter pin cannot survive the restart, and a
			// constraint-violating union cannot instantiate; either way the
			// fallback is the split, never a rejection.
			.markUnionRefused(, )
			continue
		}
		// A union extending an existing commitment upgrades it in place:
		// applyCommitment must find the largest form, or every replay merges
		// with the smaller one and re-derives the extension forever, a
		// staircase that runs a convergent program into the restart cap. The
		// upgrade keeps a template's commitments pairwise incompatible, so the
		// first compatible commitment is the only one. A union the replay
		// re-derives without extending anything is already in force: commit
		// nothing, do not restart.
		for ,  := range .commitments[] {
			if !compatibleBind(, ) {
				continue
			}
			 := maps.Clone()
			maps.Copy(, )
			if len() == len() {
				return nil // union ⊆ c: already committed
			}
			.commitments[][] = 
			return errRestart
		}
		.commitments[] = append(.commitments[], )
		return errRestart
	}
	return nil
}

func ( *solver) ( *discover.Provider,  map[*types.TypeParam]types.Type) bool {
	 := .refusedUnions[]
	return  != nil && .Contains(jointBindKey(, ))
}

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