// 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 solve import ( ) // 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. type DestInfo struct { Scanned bool // the destination is one of the scanned packages PkgName string // the destination's package name Names map[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. Imports map[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 , := range types.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 = 5000 maxTypeDepth = 40 ) // Coerce describes a value/pointer bridge applied to an argument. type Coerce int // Coercion directions for the value/pointer bridge. const ( CoerceNone Coerce = iota CoerceToPtr // value T → *T via &x CoerceToVal // *T → T via *x ) // ArgRef records how one provider input is satisfied. type ArgRef struct { isParam bool // satisfied by an injector parameter SrcType types.Type // the type whose local holds the value Coerce Coerce } // 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. type Input struct { Type types.Type Name string } // Plan is the fully resolved wiring for one set, ready for emission. type Plan struct { Name string // the set name; the generated injector func is named this pos token.Position // the set's source position, for diagnostics Order []*Instance // instances to emit, in dependency (topological) order Args map[*Instance][]ArgRef // each instance's inputs, one ArgRef per input in order Inputs []Input // injector parameters, in signature order Outputs []types.Type // value outputs, in signature order AnyCleanup bool // some provider yields a cleanup, so the injector returns an aggregated cleanup func CleanupFailable bool // some cleanup can itself fail, so the aggregated cleanup returns an error Fallible bool // some provider returns an error, so the injector returns a trailing error Lifted []*types.TypeParam // free type parameters carried onto the generic injector header ProviderCount int // 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. type solver struct { name string provs []*discover.Provider destPath string outputBase string dest *DestInfo ctxt *types.Context concrete []*Instance // concrete providers, instantiated during classification anchors []*discover.Provider resGen []*discover.Provider instances []*Instance supply gotypes.Map[types.Type, *Instance] // type → producing instance (exact), by type identity demand gotypes.Set[types.Type] // demanded types, by type identity done map[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of instantiated type-arg lists (keyed by ListKey) jointDone map[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of joint-cluster pinnings (keyed by jointBindKey) nearMissDone map[*discover.Provider]*gotypes.Set[*types.Tuple] // provider → set of bindings that failed to instantiate (constraint near-miss) skel map[*discover.Provider]*Instance // provider → memoized own-params skeleton resGenUsed map[*discover.Provider]bool nearMiss map[*discover.Provider]error // template → the type-checker error a structural match failed to instantiate with liftedMeta []liftedParam liftedNames map[string]bool avoid map[string]bool // identifiers lifted params must not equal instCount int // 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. commitments map[*discover.Provider][]map[*types.TypeParam]types.Type refusedUnions map[*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. pinnings map[*Instance]map[*types.TypeParam]types.Type } // liftedParam records a lifted free type parameter and where it came from, for // deterministic header ordering. type liftedParam struct { tp *types.TypeParam pos token.Position idx int // 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 { return diag.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]{} for range maxRestarts { := &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 , } return nil, 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 { return nil, } if := .seed(); != nil { return nil, } if := .solveDemands(); != nil { return nil, } if := .checkUnusedTemplates(); != nil { return nil, } .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() := false for , := range .valueOuts() { if gotypes.IsBareTypeParam(, ) { return diag.Errorf(.Pos, diag.ErrBareTypeParamResult, "provider %s would match every demand", .Name) } if gotypes.IsPointerToBareTypeParam(, ) { return diag.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) } if gotypes.MentionsParams(, ) { = true } } if { .resGen = append(.resGen, ) } else { .anchors = append(.anchors, ) } } return nil } // 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 } } return nil } // 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 { := false for , := range .pendingDemands() { , := .tryWorklist() if != nil { return } if { = true break } } if { continue } , := .tryJoint() if != nil { return } if { continue } break } return nil } // pendingDemands returns the still-unsatisfied demanded types, sorted. func ( *solver) () []types.Type { var []types.Type for := 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(); { return true } if , := gotypes.DualType(); { if , := .supply.At(); { return true } } return false } // 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 { return false, } if len() == 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 { return false, } } } if len() > 1 { return false, ambiguousTemplates() } if len() == 1 { := [0] return .instantiateTemplate(.prov, .bind) } return false, 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 []matchCand for , := 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-miss return nil, } } } 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(, ) { return false } } return true } // 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 []int for := 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 { return false, } if .nearMissDoneAt(, ) { return false, nil } := len(.liftedMeta) := make([]types.Type, .Tparams.Len()) var []int for := range .Tparams.Len() { := .Tparams.At() if , := []; { [] = } else { [] = .liftOne(, ) = append(, ) } } .setLiftedConstraints(, , ) if .instanceDone(, ) { .rollbackLifts() // already instantiated; undo speculative lifts return false, nil } , , := instantiate(, .ctxt, ) if != nil { return false, } 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[] = } return false, nil } for , := range .valueOuts() { if gotypes.TypeDepth() > maxTypeDepth { return false, 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 { return false, 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 { return false, } return true, 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”. return diag.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. return diag.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()) } return ambiguousProducers(, , ) } .supply.Set(, ) } for , := range .Inputs { := .typ if .flexRecv { = .flexBase // demand the value form; the dual bridge covers *S } .demand.Add() } .instances = append(.instances, ) return nil } // checkUnusedTemplates rejects result-generic templates that nothing pinned. func ( *solver) () *diag.Error { for , := range .resGen { if .resGenUsed[] { continue } if := .nearMiss[]; != nil { return diag.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.Name if != nil { = fmt.Sprintf("covered via the value/pointer bridge by %s (which produces %s)", .Prov.Name, gotypes.TypeName()) } return diag.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(), ) } return diag.Errorf(.Pos, diag.ErrUnusedTemplate, "provider %s in set %q: no consumer pins its result type: add a consumer or remove it", .Name, .name) } return nil } // 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 , , } } } return nil, 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{} if gotypes.Unify(, , , ) && .coversPinnable(, ) { return true } } } return false } // 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. var gotypes.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 { if gotypes.ContainsInvalid() { return diag.Errorf(.pos, diag.ErrInvalidType, "provider %s references %s", .Prov.Name, gotypes.TypeName()) } return nil } // --- helpers ----------------------------------------------------------------- type matchCand struct { prov *discover.Provider bind map[*types.TypeParam]types.Type } func ( []matchCand) *diag.Error { slices.SortFunc(, func(, matchCand) int { return diag.CmpPos(.prov.Pos, .prov.Pos) }) return diag.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() = } } if len() >= 2 { return ambiguousTemplatesFor(, ) } } return nil } // 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() := false for , := range .skeleton().valueOuts() { := map[*types.TypeParam]types.Type{} if !gotypes.Unify(, , , ) { continue } = true if ! && .bindInstantiates(, ) { = append(, ) = true } } } return , } func ( []*discover.Provider, types.Type) *diag.Error { slices.SortFunc(, func(, *discover.Provider) int { return diag.CmpPos(.Pos, .Pos) }) return diag.Errorf([0].Pos, diag.ErrAmbiguousTemplates, "templates %s and %s both produce %s", [0].Name, [1].Name, gotypes.TypeName()) } func (, *Instance, types.Type) *diag.Error { , := , if diag.CmpPos(.pos, .pos) < 0 { , = , } return diag.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() { return nil } := 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 := range paramSet() { if , := []; { continue } for , := range { if gotypes.MentionsParam(, ) { return false // tp appears in a value result but is unpinned } } } return true } // 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); { return CoerceToPtr // want *N, have N } return CoerceToVal // want N, have *N } func ( *solver) () token.Position { return slices.MinFunc(.provs, func(, *discover.Provider) int { return diag.CmpPos(.Pos, .Pos) }).Pos }