// Package solve is plumb's second phase: it turns the providers of one set into a// fully resolved Plan, instantiating generics by demand, inferring the injector// signature, ordering by dependency, and enforcing reachability and naming rules.// It is a deterministic function of its providers; it reads no files and consults// no clock.//// # Pipeline//// Set resolves one set by running run on a solver://// - classify: split providers into concrete / anchor / result-generic and// validate each result shape.// - seed: register the concrete providers and instantiate the anchors (lifting// their free parameters); registering an instance seeds demand from its inputs.// - solveDemands: the demand fixpoint. Each pass first works the worklist (a// demand a single result pins completely), then, if that made no progress, the// joint step (demands clustered across a template's results, lifting the// parameters no demand pins), until a whole pass adds nothing.// - checkUnusedTemplates / resolveFlexReceivers / finalize: reject a template no// consumer used, choose value-vs-pointer form for flexible struct receivers,// then resolve inputs, order instances by dependency, and build the Plan.//// # Pinning-revision restart//// A template whose pins arrive across different fixpoint rounds must be served by// one instantiation at the union of those pins, not split into half-pinned calls.// When a late pin would extend an existing instance's pinning, reviseOrSplit// commits the union and the solve restarts: it signals with the errRestart// sentinel, which rides the *diag.Error return channel up through// instantiateTemplate → viableSingle/tryWorklist/tryJoint → solveDemands → run,// compared by identity at each layer, until Set replays run on a fresh solver// with the accumulated commitments in force. Resolution is therefore a pure// function of (providers, commitments). A replay is the ordinary fixpoint, not a// mutation of the aborted attempt, and maxRestarts bounds the loop. See// revision.go.//// # Solver memo state//// Every memo table on solver is rebuilt fresh each attempt EXCEPT commitments and// refusedUnions, which persist across restarts (a committed union must stay in// force; a union already found non-viable must not re-trigger). The per-field roles// are documented on the solver struct.
package solveimport ()// DestInfo describes the destination package as far as solve's collision checks// and emit's qualification decisions need it. gen builds it; solve and emit// (which imports solve) consume it.typeDestInfostruct {Scannedbool// the destination is one of the scanned packagesPkgNamestring// the destination's package nameNamesmap[string]string// top-level identifier → base filename declaring it// Imports maps each import qualifier used in the destination's existing files // to the base name of a file using it. A generated function name (package // block) cannot coexist with an import qualifier (file block) of the same // name in any one file, so this feeds the set-name collision check.Importsmap[string]string}// ReservedNames returns the base set of identifiers a name generated for this// destination must avoid: every predeclared universe name and every destination// top-level identifier (so, in same-package mode, a generated name never shadows// a builtin the body emits or a provider the body calls unqualified). The// receiver may be nil. Callers that reserve further categories (lifted-parameter// names, import aliases, set names) add them to the returned map.func ( *DestInfo) () map[string]bool { := map[string]bool{}for , := rangetypes.Universe.Names() { [] = true }if != nil {for := range .Names { [] = true } }return}// Bounds that protect against adversarial input. They are far above any real// program and exist only so a non-terminating generic solve fails loudly rather// than looping.const (maxInstantiations = 5000maxTypeDepth = 40)// Coerce describes a value/pointer bridge applied to an argument.typeCoerceint// Coercion directions for the value/pointer bridge.const (CoerceNoneCoerce = iotaCoerceToPtr// value T → *T via &xCoerceToVal// *T → T via *x)// ArgRef records how one provider input is satisfied.typeArgRefstruct {isParambool// satisfied by an injector parameterSrcTypetypes.Type// the type whose local holds the valueCoerceCoerce}// Input is one resolved injector parameter: its type and the source-name hint// (the earliest consumer's parameter or field name, or "") used to name the// generated parameter.typeInputstruct {Typetypes.TypeNamestring}// Plan is the fully resolved wiring for one set, ready for emission.typePlanstruct {Namestring// the set name; the generated injector func is named thispostoken.Position// the set's source position, for diagnosticsOrder []*Instance// instances to emit, in dependency (topological) orderArgsmap[*Instance][]ArgRef// each instance's inputs, one ArgRef per input in orderInputs []Input// injector parameters, in signature orderOutputs []types.Type// value outputs, in signature orderAnyCleanupbool// some provider yields a cleanup, so the injector returns an aggregated cleanup funcCleanupFailablebool// some cleanup can itself fail, so the aggregated cleanup returns an errorFalliblebool// some provider returns an error, so the injector returns a trailing errorLifted []*types.TypeParam// free type parameters carried onto the generic injector headerProviderCountint// source providers in the set, for the -v report; not len(Order), the instantiation count}// InputTypes returns the injector parameter types in signature order.func ( *Plan) () []types.Type { := make([]types.Type, len(.Inputs))for , := range .Inputs { [] = .Type }return}// solver holds the mutable state of resolving one set.typesolverstruct {namestringprovs []*discover.ProviderdestPathstringoutputBasestringdest *DestInfoctxt *types.Contextconcrete []*Instance// concrete providers, instantiated during classificationanchors []*discover.ProviderresGen []*discover.Providerinstances []*Instancesupplygotypes.Map[types.Type, *Instance] // type → producing instance (exact), by type identitydemandgotypes.Set[types.Type] // demanded types, by type identitydonemap[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of instantiated type-arg lists (keyed by ListKey)jointDonemap[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of joint-cluster pinnings (keyed by jointBindKey)nearMissDonemap[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of bindings that failed to instantiate (constraint near-miss)skelmap[*discover.Provider]*Instance// provider → memoized own-params skeletonresGenUsedmap[*discover.Provider]boolnearMissmap[*discover.Provider]error// template → the type-checker error a structural match failed to instantiate withliftedMeta []liftedParamliftedNamesmap[string]boolavoidmap[string]bool// identifiers lifted params must not equalinstCountint// commitments and refusedUnions persist across pinning-revision restarts; // every other field is rebuilt fresh each attempt. commitments hold, per // template and in commitment order, the union pinnings a replay merges into // compatible bindings at instantiation time; refusedUnions memoizes unions // found non-viable or lift-entangled, so a refused merge never re-triggers.commitmentsmap[*discover.Provider][]map[*types.TypeParam]types.TyperefusedUnionsmap[*discover.Provider]*gotypes.Set[*types.Tuple]// pinnings records each template instance's pinned slots as instantiated // (after commitment merging), so the revision trigger can tell an // instance's own lifted slot from a pin that merely mentions another // instance's lifted parameter.pinningsmap[*Instance]map[*types.TypeParam]types.Type}// liftedParam records a lifted free type parameter and where it came from, for// deterministic header ordering.typeliftedParamstruct {tp *types.TypeParampostoken.Positionidxint// index within its origin provider's type parameters}// Set resolves one set into a plan. destPath is the destination import path// (what is referenced unqualified) and outputBase is the base name of the file// being overwritten, used by the set-name collision check (empty for stdout).func ( string, []*discover.Provider, , string, *DestInfo, *types.Context) (*Plan, *diag.Error) {// Sort a copy: a resolve entry point must not reorder its caller's slice. = slices.SortedStableFunc(slices.Values(), func(, *discover.Provider) int {returndiag.CmpPos(.Pos, .Pos) })// Pinning revision replays the whole solve with the commitments in force, // on a fresh solver. Resolution stays a pure function of (providers, // commitments), and a replay is the ordinary fixpoint, not a mutation of // the aborted one. Only the commitments and refusals survive a restart. := map[*discover.Provider][]map[*types.TypeParam]types.Type{} := map[*discover.Provider]*gotypes.Set[*types.Tuple]{}forrangemaxRestarts { := &solver{name: ,provs: ,destPath: ,outputBase: ,dest: ,ctxt: ,done: map[*discover.Provider]*gotypes.Set[*types.Tuple]{},jointDone: map[*discover.Provider]*gotypes.Set[*types.Tuple]{},nearMissDone: map[*discover.Provider]*gotypes.Set[*types.Tuple]{},skel: map[*discover.Provider]*Instance{},resGenUsed: map[*discover.Provider]bool{},nearMiss: map[*discover.Provider]error{},liftedNames: map[string]bool{},avoid: .ReservedNames(),commitments: ,refusedUnions: ,pinnings: map[*Instance]map[*types.TypeParam]types.Type{}, } , := .run()if == errRestart {continue }return , }returnnil, diag.Errorf([0].Pos, diag.ErrNonTerminating, "set %q: pinning revision did not converge after %d restarts", , maxRestarts)}func ( *solver) () (*Plan, *diag.Error) {if := .classify(); != nil {returnnil, }if := .seed(); != nil {returnnil, }if := .solveDemands(); != nil {returnnil, }if := .checkUnusedTemplates(); != nil {returnnil, } .resolveFlexReceivers()return .finalize()}// classify splits providers into concrete / anchor / result-generic, validating// each provider's result shape in source-position order (so the first invalid// provider by position is the one reported, regardless of class) and rejecting// bare type-parameter results.func ( *solver) () *diag.Error {for , := range .provs {if !.Generic() { , , := instantiate(, .ctxt, nil)if != nil {return }if != nil {// Unreachable for valid input: a concrete provider has no type // arguments to validate.panic(fmt.Sprintf("plumb: provider %s could not be instantiated: %v", .Name, )) } .concrete = append(.concrete, )continue } , , := instantiate(, .ctxt, ownParams())if != nil {return }if != nil {// Unreachable for valid input: the skeleton instantiation uses the // provider's own type parameters, which always satisfy their constraints.panic(fmt.Sprintf("plumb: provider %s could not be analyzed: %v", .Name, )) } := paramSet() := falsefor , := range .valueOuts() {ifgotypes.IsBareTypeParam(, ) {returndiag.Errorf(.Pos, diag.ErrBareTypeParamResult, "provider %s would match every demand", .Name) }ifgotypes.IsPointerToBareTypeParam(, ) {returndiag.Errorf(.Pos, diag.ErrBareTypeParamResult, "provider %s produces *T for a bare type parameter, matching every pointer demand and, through the value/pointer bridge, every value demand", .Name) }ifgotypes.MentionsParams(, ) { = true } }if { .resGen = append(.resGen, ) } else { .anchors = append(.anchors, ) } }returnnil}// seed registers every concrete instance and instantiates every anchor template,// registering their outputs and queuing their inputs.func ( *solver) () *diag.Error {for , := range .concrete {if := .addInstance(); != nil {return } }for , := range .anchors { := .liftAll() , , := instantiate(, .ctxt, )if != nil {return }if != nil {// Unreachable for valid input: an anchor's free parameters are lifted // to fresh parameters that carry the original constraints.panic(fmt.Sprintf("plumb: anchor template %s could not be instantiated: %v", .Name, )) }if := .addInstance(); != nil {return } }returnnil}// solveDemands runs the demand-driven instantiation fixpoint. Each successful// instantiation restarts the scan, and pendingDemands re-derives the whole demand// set each pass, so this is O(V²) in the instantiation count, bounded by// maxInstantiations and sub-millisecond for real sets (tens of instances), as with// topoOrder; a rewrite to incremental tracking would be premature.func ( *solver) () *diag.Error {for { := falsefor , := range .pendingDemands() { , := .tryWorklist()if != nil {return }if { = truebreak } }if {continue } , := .tryJoint()if != nil {return }if {continue }break }returnnil}// pendingDemands returns the still-unsatisfied demanded types, sorted.func ( *solver) () []types.Type {var []types.Typefor := range .demand.Elements {if !.satisfied() { = append(, ) } }slices.SortStableFunc(, gotypes.CmpType)return}// satisfied reports whether demand t is met by an exact or value/pointer-dual// supply.func ( *solver) ( types.Type) bool {if , := .supply.At(); {returntrue }if , := gotypes.DualType(); {if , := .supply.At(); {returntrue } }returnfalse}// tryWorklist attempts to satisfy demand d by instantiating a simple or// monadic result-generic template fully pinned by a single demand.func ( *solver) ( types.Type) (bool, *diag.Error) { , := .viableSingle()if != nil {returnfalse, }iflen() == 0 {// Try the value/pointer dual: instantiate to the dual and coerce. The // retry runs whenever d's exact form yields no viable candidate: a // structural match whose constraint rejects the pin cannot build d, so it // must not block a dual producer that can.if , := gotypes.DualType(); { , = .viableSingle()if != nil {returnfalse, } } }iflen() > 1 {returnfalse, ambiguousTemplates() }iflen() == 1 { := [0]return .instantiateTemplate(.prov, .bind) }returnfalse, nil// d stays open, for the joint step or the signature}// viableSingle returns, per template, the first value result (in result order)// that unifies with d in a covering, viable binding, so a template contributes// at most one candidate and ambiguity is decided among distinct templates. A// covering result whose binding fails the template's constraint is recorded as a// near-miss before the scan moves on: if the template ends the solve unused, the// actionable reason is its constraint rejecting the pin, not the rival that// served the demand.func ( *solver) ( types.Type) ([]matchCand, *diag.Error) {var []matchCandfor , := range .resGen { := paramSet() := .skeleton()for , := range .valueOuts() { := map[*types.TypeParam]types.Type{}if !gotypes.Unify(, , , ) || !.coversPinnable(, ) {continue }if .bindInstantiates(, ) { = append(, matchCand{prov: , bind: })break }if , := .instantiateTemplate(, ); != nil { // records the near-missreturnnil, } } }return , nil}// skeleton returns p instantiated at its own type parameters, computed once per// provider and reused across the fixpoint. The skeleton is a pure function of the// provider, and classify already proved it instantiates (panicking otherwise), so// no error or near-miss can occur here; callers only read it (valueOuts).func ( *solver) ( *discover.Provider) *Instance {if , := .skel[]; {return } , , := instantiate(, .ctxt, ownParams()) .skel[] = return}// compatibleBind reports whether two partial bindings agree on every type// parameter they share, so they describe one instantiation rather than two.func (, map[*types.TypeParam]types.Type) bool {for , := range {if , := []; && !types.Identical(, ) {returnfalse } }returntrue}// bindInstantiates reports whether p cleanly instantiates at the given partial// binding, lifting the unpinned parameters to test, then rolling those lifts back// so the probe leaves no trace. It is used to decide whether merging two demands'// pins into one cluster would poison it (a near-miss on one pin failing the whole// instantiation). Both a constraint near-miss and a hard instantiation error count// as “does not cleanly instantiate”; the real instantiation in tryJoint surfaces// either for the pin's own cluster.func ( *solver) ( *discover.Provider, map[*types.TypeParam]types.Type) bool { := len(.liftedMeta) := make([]types.Type, .Tparams.Len())var []intfor := range .Tparams.Len() { := .Tparams.At()if , := []; { [] = } else { [] = .liftOne(, ) = append(, ) } } .setLiftedConstraints(, , ) , , := instantiate(, .ctxt, ) .rollbackLifts()return == nil && == nil}// instantiateTemplate instantiates p at the given partial binding, lifting any// unpinned parameters, and registers the resulting instance. A binding that a// prior pass found to violate the template's constraint (a near-miss) is skipped:// it would lift, fail, and roll back again on every fixpoint pass, leaking a// phantom lifted parameter and inflating the instantiation count each time.//// Lifts mutate shared solver state (liftedMeta records the generated header's// type-parameter list), so a binding that does not yield a retained instance// must undo the lifts it speculatively appended; otherwise the failed attempt's// parameters survive into the signature.func ( *solver) ( *discover.Provider, map[*types.TypeParam]types.Type) (bool, *diag.Error) { = .applyCommitment(, )if := .reviseOrSplit(, ); != nil {returnfalse, }if .nearMissDoneAt(, ) {returnfalse, nil } := len(.liftedMeta) := make([]types.Type, .Tparams.Len())var []intfor := range .Tparams.Len() { := .Tparams.At()if , := []; { [] = } else { [] = .liftOne(, ) = append(, ) } } .setLiftedConstraints(, , )if .instanceDone(, ) { .rollbackLifts() // already instantiated; undo speculative liftsreturnfalse, nil } , , := instantiate(, .ctxt, )if != nil {returnfalse, }if != nil { .rollbackLifts() // constraint near-miss; undo speculative lifts .markNearMissDone(, )// miss is the type-checker's constraint violation; keep the first one so an // otherwise-unused template reports the real reason rather than a misleading // "no consumer pins its result type".if .nearMiss[] == nil { .nearMiss[] = }returnfalse, nil }for , := range .valueOuts() {ifgotypes.TypeDepth() > maxTypeDepth {returnfalse, diag.Errorf(.Pos, diag.ErrNonTerminating, "set %q: produced type %s exceeds depth %d", .name, gotypes.TypeName(), maxTypeDepth) } }// Count only retained instantiations, so the bound reflects distinct instances // rather than (now-memoized) attempts.if .instCount++; .instCount > maxInstantiations {returnfalse, diag.Errorf(.Pos, diag.ErrNonTerminating, "set %q exceeded %d instantiations; a provider appears to manufacture unboundedly larger types", .name, maxInstantiations) } .markInstanceDone(, ) .resGenUsed[] = true .pinnings[] = maps.Clone()if := .addInstance(); != nil {returnfalse, }returntrue, nil}// nearMissDoneAt reports whether binding (p, bind) was already found to violate// p's constraint. The key matches jointClusterDone's: pinned slots by type,// unpinned slots by the jointUnpinned sentinel, so it is stable across the// freshly lifted parameters an unpinned slot takes on each pass.func ( *solver) ( *discover.Provider, map[*types.TypeParam]types.Type) bool { := .nearMissDone[]return != nil && .Contains(jointBindKey(, ))}func ( *solver) ( *discover.Provider, map[*types.TypeParam]types.Type) { := .nearMissDone[]if == nil { = new(gotypes.Set[*types.Tuple]) .nearMissDone[] = } .Add(jointBindKey(, ))}// instanceDone reports whether p has already been instantiated at this exact// list of type arguments. Concrete arguments make the key stable across passes;// freshly lifted parameters are distinct objects, so a lifted instantiation is// never deduplicated here (the worklist and joint-cluster passes gate those).func ( *solver) ( *discover.Provider, []types.Type) bool { := .done[]return != nil && .Contains(gotypes.ListKey())}func ( *solver) ( *discover.Provider, []types.Type) { := .done[]if == nil { = new(gotypes.Set[*types.Tuple]) .done[] = } .Add(gotypes.ListKey())}// addInstance registers an instance's outputs and queues its inputs.func ( *solver) ( *Instance) *diag.Error {for , := range .valueOuts() {if , := .supply.At(); {if == {// One provider returns two values of the same type; name it once // rather than reporting it as colliding “with itself”.returndiag.Errorf(.pos, diag.ErrAmbiguousProducer, "provider %s produces multiple values of type %s", .Prov.Name, gotypes.TypeName()) }if .Prov == .Prov {// Two instantiations of one template: a result whose type does not // depend on the type parameter is produced identically by each, so it // collides across instances. Reporting it through ambiguousProducers // would name the same provider and position twice ("Make and Make"), // which reads as colliding with itself; explain the real cause instead.returndiag.Errorf(.pos, diag.ErrAmbiguousProducer, "provider %s produces %s at more than one instantiation; this result does not depend on the type parameter: give it a type-parameter-dependent type or split the provider", .Prov.Name, gotypes.TypeName()) }returnambiguousProducers(, , ) } .supply.Set(, ) }for , := range .Inputs { := .typif .flexRecv { = .flexBase// demand the value form; the dual bridge covers *S } .demand.Add() } .instances = append(.instances, )returnnil}// checkUnusedTemplates rejects result-generic templates that nothing pinned.func ( *solver) () *diag.Error {for , := range .resGen {if .resGenUsed[] {continue }if := .nearMiss[]; != nil {returndiag.Errorf(.Pos, diag.ErrUnusedTemplate, "provider %s in set %q is never instantiated: %w", .Name, .name, ) }if , , := .shadowedDemand(); != nil {// A direct producer supplies d itself; a bridge producer supplies d's // dual and only the value/pointer bridge yields d, so name the dual, or // the message would claim the producer makes a type it does not. := "produced by " + .Prov.Nameif != nil { = fmt.Sprintf("covered via the value/pointer bridge by %s (which produces %s)", .Prov.Name, gotypes.TypeName()) }returndiag.Errorf(.Pos, diag.ErrUnusedTemplate, "provider %s in set %q: %s is already %s, so no demand is left for it: remove the template or the other provider", .Name, .name, gotypes.TypeName(), ) }returndiag.Errorf(.Pos, diag.ErrUnusedTemplate, "provider %s in set %q: no consumer pins its result type: add a consumer or remove it", .Name, .name) }returnnil}// shadowedDemand explains the common reason a template goes unused: a consumer// does pin its result, but another provider already supplies that type, so the// demand was met before the template could run. It returns the shadowed demand,// its producer, and the dual type that producer supplies when the value/pointer// bridge (not an exact producer) is what preempted the demand, and nil when the// producer supplies the demand directly. in is nil when no such producer exists// (the result is genuinely unconsumed). Demands are scanned in CmpType order so// the named producer is deterministic.func ( *solver) ( *discover.Provider) (types.Type, *Instance, types.Type) { := paramSet() := .skeleton() := slices.SortedStableFunc(.demand.Elements, gotypes.CmpType)for , := range {if !.templateServes(, , , ) {continue }// The template would have served d; find the producer that already does: // directly, or through the value/pointer bridge over a producer of d's dual // (a *Node[int] demand covered by a Node[int] producer, or vice versa).if , := .supply.At(); && .Prov != {return , , nil }if , := gotypes.DualType(); {if , := .supply.At(); && .Prov != {return , , } } }returnnil, nil, nil}// templateServes reports whether template p could produce demand d, directly or// through the value/pointer bridge by producing d's dual, mirroring tryWorklist's// dual retry. It is the same unify+coversPinnable test viableSingle uses, so a hit// means the template really would have been a candidate for d.func ( *solver) ( *discover.Provider, *Instance, map[*types.TypeParam]bool, types.Type) bool { := []types.Type{}if , := gotypes.DualType(); { = append(, ) }for , := range {for , := range .valueOuts() { := map[*types.TypeParam]types.Type{}ifgotypes.Unify(, , , ) && .coversPinnable(, ) {returntrue } } }returnfalse}// resolveFlexReceivers fixes each struct-field provider's receiver to value or// pointer form, preferring the pointer when the set uses it elsewhere.func ( *solver) () {// Collect the exact types the set uses for non-flexible inputs and all // outputs.vargotypes.Set[types.Type]for , := range .instances {for , := range .valueOuts() { .Add() }for , := range .Inputs {if !.flexRecv { .Add(.typ) } } }for , := range .instances {for := range .Inputs {if !.Inputs[].flexRecv {continue } := types.NewPointer(.Inputs[].flexBase)if .Contains() { .Inputs[].typ = } else { .Inputs[].typ = .Inputs[].flexBase } } }}func ( *solver) ( *Instance, types.Type) *diag.Error {ifgotypes.ContainsInvalid() {returndiag.Errorf(.pos, diag.ErrInvalidType, "provider %s references %s", .Prov.Name, gotypes.TypeName()) }returnnil}// --- helpers -----------------------------------------------------------------typematchCandstruct {prov *discover.Providerbindmap[*types.TypeParam]types.Type}func ( []matchCand) *diag.Error {slices.SortFunc(, func(, matchCand) int { returndiag.CmpPos(.prov.Pos, .prov.Pos) })returndiag.Errorf([0].prov.Pos, diag.ErrAmbiguousTemplates, "templates %s and %s both match one demand", [0].prov.Name, [1].prov.Name)}// checkTemplateAmbiguity reports an error if a still-unsatisfied demand can be// produced (cleanly, under each one's constraint) by two or more distinct// templates. Such a demand has no single producer, and picking one by source// order would be arbitrary. When no template's result matches the demand's exact// form, the same test runs against its dual, and the rejection names the// produced dual form. A template that merely lifts around a demand another// provider already supplies is not in tension here: a supplied demand is no longer// pending, so it never reaches this check. This is the joint-path analog of the// worklist's single-result ambiguity test.func ( *solver) () *diag.Error {for , := range .pendingDemands() { , := .viableProducers()if ! {if , := gotypes.DualType(); { , _ = .viableProducers() = } }iflen() >= 2 {returnambiguousTemplatesFor(, ) } }returnnil}// viableProducers returns the distinct templates with some value result that// unifies with d in a viable binding, and whether any result of any template// unified at all, viable or not: the caller falls through to the dual only// when d's exact form matched nothing.func ( *solver) ( types.Type) ( []*discover.Provider, bool) {for , := range .resGen { := paramSet() := falsefor , := range .skeleton().valueOuts() { := map[*types.TypeParam]types.Type{}if !gotypes.Unify(, , , ) {continue } = trueif ! && .bindInstantiates(, ) { = append(, ) = true } } }return , }func ( []*discover.Provider, types.Type) *diag.Error {slices.SortFunc(, func(, *discover.Provider) int { returndiag.CmpPos(.Pos, .Pos) })returndiag.Errorf([0].Pos, diag.ErrAmbiguousTemplates, "templates %s and %s both produce %s", [0].Name, [1].Name, gotypes.TypeName())}func (, *Instance, types.Type) *diag.Error { , := , ifdiag.CmpPos(.pos, .pos) < 0 { , = , }returndiag.Errorf(.pos, diag.ErrAmbiguousProducer, "type %s is produced by both %s (%s) and %s (%s); plumb never picks a winner",gotypes.TypeName(), .Prov.Name, .pos, .Prov.Name, .pos)}// ownParams returns the provider's own type parameters as type arguments (used// to build the generic skeleton).func ( *discover.Provider) []types.Type {if !.Generic() {returnnil } := make([]types.Type, .Tparams.Len())for := range .Tparams.Len() { [] = .Tparams.At() }return}func ( *discover.Provider) map[*types.TypeParam]bool { := map[*types.TypeParam]bool{}if .Tparams == nil {return }for := range .Tparams.TypeParams() { [] = true }return}// coversPinnable reports whether bind pins every parameter that appears in a// value result.func ( *solver) ( *discover.Provider, map[*types.TypeParam]types.Type) bool { := .skeleton().valueOuts()for := rangeparamSet() {if , := []; {continue }for , := range {ifgotypes.MentionsParam(, ) {returnfalse// tp appears in a value result but is unpinned } } }returntrue}// bridgeDir returns the coercion that turns a value into the want form, where the// available value and want are a value/pointer dual; the direction is fixed by want.// want is unaliased first so an alias to a pointer (type P = *T) is recognized as// the pointer form, matching pointerElem/DualType, which decide the dual the same way.func ( types.Type) Coerce {if , := types.Unalias().(*types.Pointer); {returnCoerceToPtr// want *N, have N }returnCoerceToVal// want N, have *N}func ( *solver) () token.Position {returnslices.MinFunc(.provs, func(, *discover.Provider) int {returndiag.CmpPos(.Pos, .Pos) }).Pos}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)