Update go version
This commit is contained in:
60
vendor/honnef.co/go/tools/analysis/dfa/dense/flow.go
vendored
Normal file
60
vendor/honnef.co/go/tools/analysis/dfa/dense/flow.go
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package flow implements a monotone flow analysis framework.
|
||||
package dense
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"slices"
|
||||
|
||||
"honnef.co/go/tools/internal/xtools-internal/graph"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
// Analysis is the result of a monotone analysis. Fact is the type of elements
|
||||
// in the analysis semilattice, and represents the outcome of the analysis at
|
||||
// every node and edge.
|
||||
type Analysis[Fact any, NodeID comparable] struct {
|
||||
nodeMap *graph.Index[NodeID]
|
||||
ins []Fact // By NodeID
|
||||
edges []edgeFact[Fact] // Sorted by (from, to)
|
||||
}
|
||||
|
||||
// In returns the analysis fact on entry to nid. This is the merge of the facts
|
||||
// on all incoming edges.
|
||||
func (a *Analysis[Fact, NodeID]) In(nid NodeID) Fact {
|
||||
return a.ins[a.nodeMap.Index(nid)]
|
||||
}
|
||||
|
||||
// Edge returns the analysis fact propagated on edge from ==> to.
|
||||
func (a *Analysis[Fact, NodeID]) Edge(from, to NodeID) Fact {
|
||||
i, found := slices.BinarySearchFunc(a.edges, a.edge(from, to), edgeFact[Fact].compare)
|
||||
if !found {
|
||||
panic("no such edge")
|
||||
}
|
||||
return a.edges[i].fact
|
||||
}
|
||||
|
||||
func (a *Analysis[Fact, NodeID]) edge(from, to NodeID) edge {
|
||||
fromNum, toNum := a.nodeMap.Index(from), a.nodeMap.Index(to)
|
||||
return edge{fromNum, toNum}
|
||||
}
|
||||
|
||||
type edge struct {
|
||||
from, to int
|
||||
}
|
||||
|
||||
func (e edge) compare(f edge) int {
|
||||
if v := cmp.Compare(e.from, f.from); v != 0 {
|
||||
return v
|
||||
}
|
||||
return cmp.Compare(e.to, f.to)
|
||||
}
|
||||
|
||||
type edgeFact[Fact any] struct {
|
||||
edge
|
||||
fact Fact
|
||||
}
|
||||
271
vendor/honnef.co/go/tools/analysis/dfa/dense/forward.go
vendored
Normal file
271
vendor/honnef.co/go/tools/analysis/dfa/dense/forward.go
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dense
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"log"
|
||||
"slices"
|
||||
|
||||
"honnef.co/go/tools/analysis/dfa"
|
||||
"honnef.co/go/tools/internal/xtools-internal/graph"
|
||||
)
|
||||
|
||||
// Forward performs a forward monotone analysis over a control flow graph.
|
||||
//
|
||||
// The entry map provides initial state for entry blocks (blocks with zero
|
||||
// predecessors). For each edge, it calls transfer(fact, edge), where fact is
|
||||
// the analysis state on entry to edge.Pred. The transfer function must return
|
||||
// the outgoing analysis state of the edge (which may be fact, if the edge has
|
||||
// no effect on the analysis state).
|
||||
func Forward[L dfa.Semilattice[Fact], Fact any, NodeID comparable](g graph.Graph[NodeID], entry map[NodeID]Fact, transfer func(from, to NodeID, fact Fact) Fact) *Analysis[Fact, NodeID] {
|
||||
cg, nodeMap := graph.Compact(g)
|
||||
|
||||
nNodes := cg.NumNodes()
|
||||
fb := &fwdBuilder[L, Fact, NodeID]{
|
||||
cfg: cg,
|
||||
nodeMap: nodeMap,
|
||||
transfer: transfer,
|
||||
blocks: make([]blockInfo[Fact], nNodes),
|
||||
}
|
||||
fb.queue.init(cg)
|
||||
|
||||
// Initialize each node.
|
||||
totalEdges := 0
|
||||
for ni := range nNodes {
|
||||
b := &fb.blocks[ni]
|
||||
|
||||
// Construct back-edges.
|
||||
//
|
||||
// I experimented with making Graph support iterating over in-edges, but
|
||||
// in practice that just meant each Graph implementation had a copy of
|
||||
// this logic. So instead we keep Graph as simple as possible and
|
||||
// compute the auxiliary data in the algorithm. One drawback of this is
|
||||
// that, for the [Transpose] graph, this information is redundant with
|
||||
// the underlying graph. We could potentially special-case that.
|
||||
outs := 0
|
||||
for succID := range cg.Out(ni) {
|
||||
succ := &fb.blocks[succID]
|
||||
succ.preds = append(succ.preds, blockEdge{ni, outs})
|
||||
outs++
|
||||
totalEdges++
|
||||
}
|
||||
|
||||
// Initialize in & out states.
|
||||
fact, ok := entry[nodeMap.Value(ni)]
|
||||
if !ok {
|
||||
fact = fb.l.Ident()
|
||||
}
|
||||
b.in = fact
|
||||
b.out = slices.Repeat([]Fact{fb.l.Ident()}, outs)
|
||||
|
||||
// Enqueue block.
|
||||
//
|
||||
// It's tempting to enqueue only the entry blocks, but this is wrong.
|
||||
// The entry map may be empty if there are no interesting entry states,
|
||||
// but the transfer function may still introduce interesting states
|
||||
// anywhere.
|
||||
b.dirty = true
|
||||
fb.queue.enqueue(ni)
|
||||
}
|
||||
|
||||
// Propagate over blocks.
|
||||
fb.propagate()
|
||||
|
||||
// Collect the final analysis results.
|
||||
a := Analysis[Fact, NodeID]{
|
||||
nodeMap: nodeMap,
|
||||
ins: make([]Fact, nNodes),
|
||||
edges: make([]edgeFact[Fact], 0, totalEdges),
|
||||
}
|
||||
for pred := range nNodes {
|
||||
a.ins[pred] = fb.blocks[pred].in
|
||||
i := 0
|
||||
for succ := range cg.Out(pred) {
|
||||
edge := edge{pred, succ}
|
||||
a.edges = append(a.edges, edgeFact[Fact]{edge, fb.blocks[pred].out[i]})
|
||||
i++
|
||||
}
|
||||
}
|
||||
slices.SortFunc(a.edges, func(a, b edgeFact[Fact]) int { return a.edge.compare(b.edge) })
|
||||
return &a
|
||||
}
|
||||
|
||||
// fwdBuilder is the state used during [Forward] analysis.
|
||||
type fwdBuilder[L dfa.Semilattice[Fact], Fact any, NodeID comparable] struct {
|
||||
l L // Lattice
|
||||
|
||||
cfg graph.Graph[int] // Control flow graph (compact)
|
||||
nodeMap *graph.Index[NodeID] // Map from cfg to original NodeIDs
|
||||
|
||||
// transfer is the edge transfer function.
|
||||
transfer func(from, to NodeID, fact Fact) Fact
|
||||
|
||||
blocks []blockInfo[Fact]
|
||||
|
||||
queue nodeHeap
|
||||
}
|
||||
|
||||
type blockInfo[Fact any] struct {
|
||||
dirty bool // The in fact has never been propagated.
|
||||
|
||||
preds []blockEdge
|
||||
|
||||
in Fact
|
||||
out []Fact // Corresponds to i'th out edge
|
||||
}
|
||||
|
||||
type blockEdge struct {
|
||||
node int
|
||||
i int // Out edge index
|
||||
}
|
||||
|
||||
// nodeHeap implements a heap of NodeIDs, ordered topologically.
|
||||
//
|
||||
// We use this ordering so forward analysis converges more quickly.
|
||||
type nodeHeap struct {
|
||||
heap []int // Remaining nodes in the current sweep
|
||||
deferred []int // Nodes of next sweep
|
||||
inQueue []int64 // Bitmap over node IDs
|
||||
prio []int // NodeID -> priority
|
||||
currentPrio int // Priority of last dequeued node, or -1
|
||||
}
|
||||
|
||||
func (h *nodeHeap) init(g graph.Graph[int]) {
|
||||
nNodes := g.NumNodes()
|
||||
*h = nodeHeap{
|
||||
inQueue: make([]int64, (nNodes+63)/64),
|
||||
prio: make([]int, nNodes),
|
||||
currentPrio: -1,
|
||||
}
|
||||
for p, nid := range graph.ReversePostorder(g) {
|
||||
h.prio[nid] = p
|
||||
}
|
||||
}
|
||||
|
||||
func (h *nodeHeap) enqueue(nid int) {
|
||||
if h.inQueue[nid/64]&(1<<(nid%64)) != 0 {
|
||||
return
|
||||
}
|
||||
h.inQueue[nid/64] |= 1 << (nid % 64)
|
||||
|
||||
if h.currentPrio >= 0 && h.prio[nid] <= h.currentPrio {
|
||||
// This is a retreating edge, self-edge, or other update to a node
|
||||
// already passed in this sweep. Coalesce it into the next sweep.
|
||||
h.deferred = append(h.deferred, nid)
|
||||
} else {
|
||||
heap.Push(h, nid)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *nodeHeap) dequeue() int {
|
||||
if len(h.heap) == 0 {
|
||||
// Start the next RPO sweep.
|
||||
h.heap, h.deferred = h.deferred, h.heap[:0]
|
||||
h.currentPrio = -1
|
||||
heap.Init(h)
|
||||
}
|
||||
|
||||
nid := h.heap[0]
|
||||
heap.Pop(h)
|
||||
h.inQueue[nid/64] &^= 1 << (nid % 64)
|
||||
h.currentPrio = h.prio[nid]
|
||||
return nid
|
||||
}
|
||||
|
||||
func (h *nodeHeap) pending() bool { return len(h.heap) != 0 || len(h.deferred) != 0 }
|
||||
func (h nodeHeap) Len() int { return len(h.heap) }
|
||||
func (h nodeHeap) Less(i, j int) bool { return h.prio[h.heap[i]] < h.prio[h.heap[j]] }
|
||||
func (h nodeHeap) Swap(i, j int) { h.heap[i], h.heap[j] = h.heap[j], h.heap[i] }
|
||||
func (h *nodeHeap) Push(x any) { h.heap = append(h.heap, x.(int)) }
|
||||
func (h *nodeHeap) Pop() any {
|
||||
n := len(h.heap)
|
||||
x := h.heap[n-1]
|
||||
h.heap = h.heap[:n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
func (fb *fwdBuilder[L, Fact, NodeID]) merge(a, b Fact) Fact {
|
||||
if fb.l.Equals(a, b) {
|
||||
return a
|
||||
}
|
||||
return fb.l.Merge(a, b)
|
||||
}
|
||||
|
||||
func (fb *fwdBuilder[L, Fact, NodeID]) propagate() {
|
||||
for fb.queue.pending() {
|
||||
bi := fb.queue.dequeue()
|
||||
block := &fb.blocks[bi]
|
||||
|
||||
// Merge predecessor facts to compute updated "in" fact.
|
||||
var in Fact
|
||||
first := true
|
||||
for _, edge := range block.preds {
|
||||
pred := &fb.blocks[edge.node]
|
||||
var edgeFact Fact
|
||||
if pred.dirty {
|
||||
// We haven't visited this predecessor yet, so it doesn't have
|
||||
// meaningful out facts.
|
||||
edgeFact = fb.l.Ident()
|
||||
} else {
|
||||
edgeFact = pred.out[edge.i]
|
||||
}
|
||||
if first {
|
||||
if debug {
|
||||
log.Printf("propagate to node %d", bi)
|
||||
}
|
||||
in = edgeFact
|
||||
first = false
|
||||
} else {
|
||||
in = fb.merge(in, edgeFact)
|
||||
}
|
||||
if debug {
|
||||
log.Printf(" from node %d: %v", edge.node, edgeFact)
|
||||
}
|
||||
}
|
||||
if first {
|
||||
// No predecessors.
|
||||
if debug {
|
||||
log.Printf("node %d gets initial state", bi)
|
||||
}
|
||||
in = block.in
|
||||
}
|
||||
|
||||
if !block.dirty && fb.l.Equals(in, block.in) {
|
||||
// No change to block input, which means the transfer function
|
||||
// results also won't change from the last time we ran it.
|
||||
if debug {
|
||||
log.Printf(" initial state unchanged: %v", in)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
log.Printf(" new initial state: %v", in)
|
||||
}
|
||||
block.in = in
|
||||
|
||||
// Apply transfer function.
|
||||
predID := fb.nodeMap.Value(bi)
|
||||
i := 0
|
||||
for succNum := range fb.cfg.Out(bi) {
|
||||
edgeFact := fb.transfer(predID, fb.nodeMap.Value(succNum), in)
|
||||
if block.dirty || !fb.l.Equals(block.out[i], edgeFact) {
|
||||
// Out fact changed, so recompute the target block.
|
||||
if debug {
|
||||
log.Printf(" to node %d: %v", succNum, edgeFact)
|
||||
}
|
||||
block.out[i] = edgeFact
|
||||
fb.queue.enqueue(succNum)
|
||||
} else {
|
||||
if debug {
|
||||
log.Printf(" to node %d: no change", succNum)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
block.dirty = false
|
||||
}
|
||||
}
|
||||
48
vendor/honnef.co/go/tools/analysis/dfa/dot.go
vendored
Normal file
48
vendor/honnef.co/go/tools/analysis/dfa/dot.go
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package dfa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dot returns a directed graph in [Graphviz] format that represents the finite
|
||||
// join-semilattice ⟨S, ≤⟩. Vertices represent elements in S and edges
|
||||
// represent the ≤ relation between elements. We map from ⟨S, ∨⟩ to ⟨S, ≤⟩ by
|
||||
// computing x ∨ y for all elements in [S]², where x ≤ y iff x ∨ y == y.
|
||||
//
|
||||
// The resulting graph can be filtered through [tred] to compute the transitive
|
||||
// reduction of the graph, the visualisation of which corresponds to the Hasse
|
||||
// diagram of the semilattice.
|
||||
//
|
||||
// [Graphviz]: https://graphviz.org/
|
||||
// [tred]: https://graphviz.org/docs/cli/tred/
|
||||
func Dot[L Semilattice[Elem], Elem any](states []Elem) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("digraph{\n")
|
||||
sb.WriteString("rankdir=\"BT\"\n")
|
||||
|
||||
for i, v := range states {
|
||||
if vs, ok := any(v).(fmt.Stringer); ok {
|
||||
fmt.Fprintf(&sb, "n%d [label=%q]\n", i, vs)
|
||||
} else {
|
||||
fmt.Fprintf(&sb, "n%d [label=%q]\n", i, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
var l L
|
||||
|
||||
for dx, x := range states {
|
||||
for dy, y := range states {
|
||||
if dx == dy {
|
||||
continue
|
||||
}
|
||||
|
||||
if l.Equals(l.Merge(x, y), y) {
|
||||
fmt.Fprintf(&sb, "n%d -> n%d\n", dx, dy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("}")
|
||||
return sb.String()
|
||||
}
|
||||
147
vendor/honnef.co/go/tools/analysis/dfa/lattice.go
vendored
Normal file
147
vendor/honnef.co/go/tools/analysis/dfa/lattice.go
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dfa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// A Semilattice describes a bounded semilattice over Elem.
|
||||
// That is, a partial order over values of type Elem, with a binary
|
||||
// Merge operator and an identity element.
|
||||
//
|
||||
// This is typically implemented by a stateless type, and acts as a factory for
|
||||
// lattice elements.
|
||||
type Semilattice[Elem any] interface {
|
||||
// Ident returns the identity element of this lattice, that is the unit of
|
||||
// the Merge operation.
|
||||
Ident() Elem
|
||||
|
||||
// Equals returns whether a and b are the same element.
|
||||
Equals(a, b Elem) bool
|
||||
|
||||
// Merge combines two lattice values, such as the two possible values of a
|
||||
// variable at the end of an if/else statement.
|
||||
//
|
||||
// Merge must satisfy the following identities, where we use ∧ for Merge, =
|
||||
// for Equals, and 𝟏 for Ident:
|
||||
//
|
||||
// - Associativity: x ∧ (y ∧ z) = (x ∧ y) ∧ z
|
||||
// - Commutativity: x ∧ y = y ∧ x
|
||||
// - Idempotency: x ∧ x = x
|
||||
// - Identity: x ∧ 𝟏 = x
|
||||
Merge(a, b Elem) Elem
|
||||
}
|
||||
|
||||
// A MapLattice implements [Semilattice][map[Key]Elem]. The values in the map
|
||||
// are themselves defined by [Semilattice] L.
|
||||
//
|
||||
// Any elements missing from the map are implicitly L's identity element, and
|
||||
// L's identity element never appears as a value in the map.
|
||||
//
|
||||
// For densely numbered keys, consider using [DenseMapLattice] instead.
|
||||
type MapLattice[Key comparable, Elem any, L Semilattice[Elem]] struct {
|
||||
l L
|
||||
}
|
||||
|
||||
func (m MapLattice[Key, Elem, L]) Ident() map[Key]Elem {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MapLattice[Key, Elem, L]) Equals(a, b map[Key]Elem) bool {
|
||||
return maps.EqualFunc(a, b, m.l.Equals)
|
||||
}
|
||||
|
||||
func (m MapLattice[Key, Elem, L]) Merge(a, b map[Key]Elem) map[Key]Elem {
|
||||
if len(a) == 0 {
|
||||
return b
|
||||
} else if len(b) == 0 {
|
||||
return a
|
||||
}
|
||||
|
||||
// We need to consider the union of keys in a and b.
|
||||
out := make(map[Key]Elem)
|
||||
id := m.l.Ident()
|
||||
for k, av := range a {
|
||||
bv, ok := b[k]
|
||||
if !ok {
|
||||
// Because Merge(x, Ident()) == x, we can skip calling L.Merge.
|
||||
out[k] = av
|
||||
continue
|
||||
}
|
||||
|
||||
w := m.l.Merge(av, bv)
|
||||
if m.l.Equals(w, id) {
|
||||
// In a semilattice, Merge(x, y) = Ident is only possible when x ==
|
||||
// Ident and y == Ident.
|
||||
panic(fmt.Sprintf(
|
||||
"%T is not a semilattice: Merge(%v, %v) returned Ident for non-Ident arguments",
|
||||
m.l, av, bv))
|
||||
}
|
||||
out[k] = w
|
||||
}
|
||||
// We considered keys that are only in a, and in both a and b. Now we just
|
||||
// need to handle keys that are only in b.
|
||||
for k, v2 := range b {
|
||||
if _, ok := a[k]; !ok {
|
||||
out[k] = v2
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// A DenseMapLattice implements [Semilattice][[]Elem]. It is like a [MapLattice]
|
||||
// that is indexed by integers. The values in the map are themselves defined by
|
||||
// [Semilattice] L.
|
||||
//
|
||||
// Unlike [MapLattice], L's identity element may appear as a value in the map,
|
||||
// to allow for gaps in the numbering of keys when the identity element is
|
||||
// Elem's zero value.
|
||||
type DenseMapLattice[Elem any, L Semilattice[Elem]] struct {
|
||||
l L
|
||||
}
|
||||
|
||||
func (s DenseMapLattice[Elem, L]) Ident() []Elem {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s DenseMapLattice[Elem, L]) Equals(a, b []Elem) bool {
|
||||
nmin := min(len(a), len(b))
|
||||
ident := s.l.Ident()
|
||||
|
||||
// Check that up to nmin, all elements in a and b match. If one of a or b
|
||||
// is longer, then its tail nmin:nmax must only contain identity elements.
|
||||
return slices.EqualFunc(a[:nmin], b[:nmin], s.l.Equals) &&
|
||||
!slices.ContainsFunc(a[nmin:], func(e Elem) bool {
|
||||
return !s.l.Equals(e, ident)
|
||||
}) &&
|
||||
!slices.ContainsFunc(b[nmin:], func(e Elem) bool {
|
||||
return !s.l.Equals(e, ident)
|
||||
})
|
||||
}
|
||||
|
||||
func (s DenseMapLattice[Elem, L]) Merge(a, b []Elem) []Elem {
|
||||
if len(a) == 0 {
|
||||
return b
|
||||
} else if len(b) == 0 {
|
||||
return a
|
||||
}
|
||||
out := make([]Elem, max(len(a), len(b)))
|
||||
for k := range max(len(a), len(b)) {
|
||||
av := s.l.Ident()
|
||||
bv := s.l.Ident()
|
||||
if k < len(a) {
|
||||
av = a[k]
|
||||
}
|
||||
if k < len(b) {
|
||||
bv = b[k]
|
||||
}
|
||||
out[k] = s.l.Merge(av, bv)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user