Source File
joint.go
Belonging Package
go.pact.im/x/plumb/internal/solve
package solveimport ()// 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 {:= falsefor , := 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.Typefor , := 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).:= falsefor , := range {for , := range {if !compatibleBind(, ) {continue}:= maps.Clone()maps.Copy(, )if !.bindInstantiates(, ) {continue}maps.Copy(, )= truebreak}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(, , , ) {= trueif 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()}
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64)