Files
raven/vendor/honnef.co/go/tools/analysis/dfa/lattice.go
2026-08-21 10:49:49 +00:00

148 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}