package solveimport ()// topoOrder returns the instances in dependency order: every producer precedes// the consumers of its outputs. Ties are broken by source position so the order// is deterministic and independent of how packages and files were read. A cycle// is reported as a located path.//// The selection loop below is O(V²·arity): each emitted node rescans all// instances and re-checks their dependency sets. That is quadratic, but V is// bounded by maxInstantiations and tiny for real sets (tens of instances; a// deepening generic template trips maxTypeDepth long before it manufactures// thousands of instances), so ordering stays sub-millisecond in practice. Kahn's// O(V+E) would be premature. The rescan also gives the deterministic// position-ordered pick and feeds the cycle report for free.func ( *solver) ( *Plan) ([]*Instance, *diag.Error) { := slices.SortedStableFunc(slices.Values(.instances), func(, *Instance) int { returndiag.CmpPos(.pos, .pos) })// deps[in] = the producer instances in must follow. := map[*Instance]map[*Instance]bool{}for , := range { [] = map[*Instance]bool{}for , := range .Args[] {if .isParam {continue }// A self-edge (a provider consuming a type it produces, directly or // via the value/pointer bridge) is a single-node cycle, so it is kept.if , := .supply.At(.SrcType); { [][] = true } } } := map[*Instance]bool{}var []*Instanceforlen() < len() { := falsefor , := range { // already position-sorted: deterministic pickif [] {continue } := truefor := range [] {if ![] { = falsebreak } }if { [] = true = append(, ) = truebreak } }if ! {returnnil, .cycleError(, , , ) } }return , nil}// cycleError finds a dependency cycle among the not-yet-emitted instances and// reports it as a path, naming the type each provider needs from the next.func ( *solver) ( *Plan, []*Instance, map[*Instance]bool, map[*Instance]map[*Instance]bool) *diag.Error {var []*Instancefor , := range {if ![] { = append(, ) } }const ( = iota// unvisited// on the current DFS stack// fully explored ) := map[*Instance]int{}var []*Instancevar []*Instancevarfunc( *Instance) bool = func( *Instance) bool { [] = = append(, )// deterministic neighbor ordervar []*Instancefor := range [] {if ![] { = append(, ) } }slices.SortStableFunc(, func(, *Instance) int { returndiag.CmpPos(.pos, .pos) })for , := range {switch [] {case :// found a cycle: slice the stack from d onwardfor , := rangeslices.Backward() {if == { = append([]*Instance{}, [:]...)returntrue } }returntruecase :if () {returntrue } } } [] = = [:len()-1]returnfalse }for , := range {if [] == {if () {break } } }iflen() == 0 {// Should be unreachable: no progress implies a cycle exists.panic("plumb: ordering stalled without a detectable cycle") }// Render the cycle as "A needs T1 → B needs T2 → A", naming on each edge the // type the provider consumes from the next one round the loop.var []stringfor , := range { := [(+1)%len()]if , := .edgeType(, , ); != nil { := gotypes.TypeName()if { += " (bridged)" } = append(, fmt.Sprintf("%s needs %s", .Prov.Name, )) } else { = append(, .Prov.Name) } } = append(, [0].Prov.Name)returndiag.Errorf([0].pos, diag.ErrDependencyCycle, "set %q: %s", .name, strings.Join(, " → "))}// edgeType returns the type consumer declares as the input that producer supplies// (the dependency that makes consumer follow producer) and whether that input is// satisfied through the value/pointer bridge. It reports the consumer's demanded// type (its InputSlot), not the producer's SrcType, so the diagnostic names the// type as written in the source; on a bridged edge the two are duals. The type is// nil if no such edge exists (defensive; cycle edges always have one).func ( *solver) ( *Plan, , *Instance) (types.Type, bool) {for , := range .Args[] {if .isParam {continue }if , := .supply.At(.SrcType); && == {return .Inputs[].typ, .Coerce != CoerceNone } }returnnil, false}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)