src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

nilness.go (22650B)


      1 package nilness
      2 
      3 import (
      4 	"fmt"
      5 	"go/constant"
      6 	"go/token"
      7 	"go/types"
      8 	"reflect"
      9 	"slices"
     10 	"strings"
     11 
     12 	"honnef.co/go/tools/analysis/dfa"
     13 	"honnef.co/go/tools/analysis/dfa/dense"
     14 	"honnef.co/go/tools/go/ir"
     15 	"honnef.co/go/tools/go/types/typeutil"
     16 	"honnef.co/go/tools/internal/passes/buildir"
     17 
     18 	"golang.org/x/exp/typeparams"
     19 	"golang.org/x/tools/go/analysis"
     20 )
     21 
     22 // TODO(dh): The analysis is currently entirely forward, which means that for
     23 //
     24 // 	x := s[:0]
     25 // 	y := s[:1]
     26 // 	z := s[:0]
     27 //
     28 // x will have MaybeNil at every program point and s will have MaybeNil before
     29 // execution of y, even though executing y without panicing tells us that s has
     30 // been non-nil for all 3 instructions.
     31 
     32 type nilnessFact struct {
     33 	Rets []ValueNilness
     34 }
     35 
     36 func (*nilnessFact) AFact() {}
     37 func (fact *nilnessFact) String() string {
     38 	return fmt.Sprintf("nilness: %v", fact.Rets)
     39 }
     40 
     41 type ValueNilness struct {
     42 	// Undefined for non-interface values.
     43 	// For interface values, whether the stored value may be nil.
     44 	// Even when Outer == MaybeNil, Inner may still offer precise information
     45 	// for the cases when Outer is dynamically not nil. For example, {NeverNil,
     46 	// MaybeNil} states that the interface value might be nil, but if it isn't,
     47 	// it will definitely contain a non-nil value.
     48 	Inner Nilness
     49 	// For non-interface values, whether the value may be nil.
     50 	// For interface values, whether the interface value may be nil.
     51 	Outer Nilness
     52 }
     53 
     54 type Result struct {
     55 	m map[*types.Func][]ValueNilness
     56 }
     57 
     58 var Analysis = &analysis.Analyzer{
     59 	Name:       "nilness",
     60 	Doc:        "Annotates return values with their nilness",
     61 	Run:        run,
     62 	Requires:   []*analysis.Analyzer{buildir.Analyzer},
     63 	FactTypes:  []analysis.Fact{(*nilnessFact)(nil)},
     64 	ResultType: reflect.TypeFor[*Result](),
     65 }
     66 
     67 // Nilness returns nilness information for return value ret of fn.
     68 func (r *Result) Nilness(fn *types.Func, ret int) ValueNilness {
     69 	typ := fn.Type().(*types.Signature).Results().At(ret).Type()
     70 	if !typeutil.IsPointerLike(typ) {
     71 		return ValueNilness{Outer: NeverNil}
     72 	}
     73 	if len(r.m[fn]) == 0 {
     74 		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
     75 	}
     76 
     77 	return normalize(r.m[fn][ret], typ)
     78 }
     79 
     80 func normalize(v ValueNilness, typ types.Type) ValueNilness {
     81 	if v.Inner == 0 || !types.IsInterface(typ) {
     82 		v.Inner = MaybeNil
     83 	}
     84 	if v.Outer == 0 {
     85 		v.Outer = MaybeNil
     86 	}
     87 	return v
     88 }
     89 
     90 func run(pass *analysis.Pass) (any, error) {
     91 	seen := map[*ir.Function]struct{}{}
     92 	out := &Result{
     93 		m: map[*types.Func][]ValueNilness{},
     94 	}
     95 
     96 	// TODO(dh): instead of recursion and giving up on mutual recursion, we
     97 	// should compute the DFA over the call graph, at least until we have
     98 	// proper function summaries.
     99 	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
    100 		impl(pass, fn, seen)
    101 	}
    102 
    103 	for _, fact := range pass.AllObjectFacts() {
    104 		out.m[fact.Object.(*types.Func)] = fact.Fact.(*nilnessFact).Rets
    105 	}
    106 
    107 	return out, nil
    108 }
    109 
    110 type Nilness uint8
    111 
    112 const (
    113 	// The value is never nil.
    114 	NeverNil Nilness = iota + 1
    115 	// The value is always nil.
    116 	AlwaysNil
    117 	// The value might be nil, but only because of the value of a global
    118 	// variable.
    119 	MaybeNilGlobal
    120 	// The value might be nil.
    121 	MaybeNil
    122 )
    123 
    124 func (n Nilness) String() string {
    125 	switch n {
    126 	case 0:
    127 		return "NoNilness"
    128 	case NeverNil:
    129 		return "NeverNil"
    130 	case AlwaysNil:
    131 		return "AlwaysNil"
    132 	case MaybeNilGlobal:
    133 		return "MaybeNilGlobal"
    134 	case MaybeNil:
    135 		return "MaybeNil"
    136 	default:
    137 		return "InvalidNilness"
    138 	}
    139 }
    140 
    141 type state struct {
    142 	cloned bool
    143 	m      []ValueNilness
    144 	n      numbering
    145 }
    146 
    147 func (s *state) get(v ir.Value) ValueNilness {
    148 	if !typeutil.IsPointerLike(v.Type()) {
    149 		// All non-pointer-like types are always {_ NeverNil}.
    150 		return ValueNilness{Outer: NeverNil}
    151 	}
    152 	num := s.n.number(v)
    153 	if num < len(s.m) {
    154 		return s.m[num]
    155 	}
    156 
    157 	switch v.(type) {
    158 	case *ir.Parameter:
    159 		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
    160 	case *ir.Builtin:
    161 		return ValueNilness{Outer: NeverNil}
    162 	case *ir.FreeVar:
    163 		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
    164 	case *ir.Function:
    165 		return ValueNilness{Outer: NeverNil}
    166 	case *ir.Global:
    167 		// Globals are addresses, not the values stored in them. The addresses
    168 		// cannot be nil.
    169 		return ValueNilness{Outer: NeverNil}
    170 	}
    171 
    172 	return lattice{}.Ident()
    173 }
    174 
    175 func (s *state) set(key ir.Value, value ValueNilness) {
    176 	if !typeutil.IsPointerLike(key.Type()) {
    177 		// No point in recording state for non-pointer-like types. They're
    178 		// always {_ NeverNil}.
    179 		return
    180 	}
    181 
    182 	if value == (lattice{}.Ident()) {
    183 		// No point in storing the default value.
    184 		return
    185 	}
    186 	num := s.n.number(key)
    187 	if !s.cloned {
    188 		if num < len(s.m) && s.m[num] == value {
    189 			// Don't clone if the value already matches.
    190 			return
    191 		}
    192 		s.cloned = true
    193 		s.m = slices.Clone(s.m)
    194 	}
    195 	if num >= len(s.m) {
    196 		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
    197 	}
    198 	s.m[num] = value
    199 }
    200 
    201 func (s *state) setInner(key ir.Value, value Nilness) {
    202 	if !typeutil.IsPointerLike(key.Type()) {
    203 		return
    204 	}
    205 	if value == (lattice{}.Ident().Inner) {
    206 		return
    207 	}
    208 	num := s.n.number(key)
    209 	if !s.cloned {
    210 		if num < len(s.m) && s.m[num].Inner == value {
    211 			// Don't clone if the value already matches.
    212 			return
    213 		}
    214 		s.cloned = true
    215 		s.m = slices.Clone(s.m)
    216 	}
    217 	if num >= len(s.m) {
    218 		dflt := s.get(key)
    219 		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
    220 		s.m[num] = dflt
    221 	}
    222 	v := s.m[num]
    223 	v.Inner = value
    224 	s.m[num] = v
    225 }
    226 
    227 func (s *state) setOuter(key ir.Value, value Nilness) {
    228 	if !typeutil.IsPointerLike(key.Type()) {
    229 		return
    230 	}
    231 	if value == (lattice{}).Ident().Outer {
    232 		return
    233 	}
    234 	num := s.n.number(key)
    235 	if !s.cloned {
    236 		if num < len(s.m) && s.m[num].Outer == value {
    237 			// Don't clone if the value already matches.
    238 			return
    239 		}
    240 		s.cloned = true
    241 		s.m = slices.Clone(s.m)
    242 	}
    243 	if num >= len(s.m) {
    244 		dflt := s.get(key)
    245 		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
    246 		s.m[num] = dflt
    247 	}
    248 	v := s.m[num]
    249 	v.Outer = value
    250 	s.m[num] = v
    251 }
    252 
    253 func defaultNilnessForSignature(pass *analysis.Pass, typ *types.Signature) []ValueNilness {
    254 	n := typ.Results().Len()
    255 	if n == 0 {
    256 		return nil
    257 	}
    258 	out := make([]ValueNilness, n)
    259 	for i := range n {
    260 		out[i] = defaultNilness(pass, typ.Results().At(i).Type())
    261 	}
    262 	return out
    263 }
    264 
    265 func defaultNilness(pass *analysis.Pass, typ types.Type) ValueNilness {
    266 	if typeutil.IsPointerLike(typ) {
    267 		// IsPointerLike handles type parameters with type sets, too.
    268 		return ValueNilness{MaybeNil, MaybeNil}
    269 	} else {
    270 		return ValueNilness{NeverNil, NeverNil}
    271 	}
    272 }
    273 
    274 func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) []ValueNilness {
    275 	goto start
    276 bailout:
    277 	return defaultNilnessForSignature(pass, fn.Signature)
    278 
    279 start:
    280 	if fn.Signature.Results().Len() == 0 {
    281 		return nil
    282 	}
    283 	if fn.Object() == nil {
    284 		// TODO(dh): support closures
    285 		goto bailout
    286 	}
    287 	if fact := new(nilnessFact); pass.ImportObjectFact(fn.Object(), fact) {
    288 		return fact.Rets
    289 	}
    290 	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
    291 		goto bailout
    292 	}
    293 	if fn.Blocks == nil {
    294 		goto bailout
    295 	}
    296 	if _, ok := seenFns[fn]; ok {
    297 		// break recursion
    298 		goto bailout
    299 	}
    300 
    301 	seenFns[fn] = struct{}{}
    302 
    303 	anyPointers := false
    304 	for ret := range fn.Signature.Results().Variables() {
    305 		if typeutil.IsPointerLike(ret.Type()) {
    306 			anyPointers = true
    307 			break
    308 		}
    309 	}
    310 
    311 	if !anyPointers {
    312 		goto bailout
    313 	}
    314 
    315 	n := numbering{}
    316 
    317 	processBlock := func(from, to *ir.BasicBlock, s state) state {
    318 		handleReturnValue := func(v ir.Value, call *ir.Call, idx int) {
    319 			typ := call.Common().Signature().Results().At(idx).Type()
    320 			if !typeutil.IsPointerLike(typ) {
    321 				s.setOuter(v, NeverNil)
    322 				return
    323 			}
    324 
    325 			if callee, ok := call.Call.Value.(*ir.Builtin); ok {
    326 				switch callee.Name() {
    327 				case "append":
    328 					// TODO(dh): if we knew that the varargs had non-zero
    329 					// length, we'd know that the resulting slice is non-nil.
    330 					switch an := s.get(call.Call.Args[0]).Outer; an {
    331 					case MaybeNil, MaybeNilGlobal, NeverNil:
    332 						s.setOuter(v, an)
    333 					case AlwaysNil:
    334 						s.setOuter(v, MaybeNil)
    335 					}
    336 				case "UnsafeSlice":
    337 					// If len is negative, or if ptr is nil and len is not
    338 					// zero, unsafe.Slice panics. This implies that a non-nil
    339 					// pointer cannot become nil, and vice versa.
    340 					s.set(v, s.get(call.Call.Args[0]))
    341 				case "UnsafeStringData":
    342 					// TODO(dh): if we had string length information we could
    343 					// return better information.
    344 					s.setOuter(v, MaybeNil)
    345 				case "UnsafeSliceData":
    346 					// When the slice is non-nil but has zero capacity, the
    347 					// returned pointer is still non-nil, so we don't have to
    348 					// worry about that.
    349 					s.set(v, s.get(call.Call.Args[0]))
    350 				case "UnsafeAdd":
    351 					// TODO(dh): a positive addend can never result in a nil pointer.
    352 
    353 					// Pointer arithmetic can turn nil pointers into non-nil
    354 					// ones and vice versa.
    355 					s.setOuter(v, MaybeNil)
    356 				case "ssa:deferstack":
    357 					s.setOuter(v, NeverNil)
    358 				case "ssa:wrapnilchk":
    359 					s.setOuter(v, NeverNil)
    360 				case "recover":
    361 					s.setOuter(v, MaybeNil)
    362 				default:
    363 					panic(fmt.Sprintf("internal error: unhandled builtin %s", callee.Name()))
    364 				}
    365 				return
    366 			}
    367 
    368 			callee := call.Common().StaticCallee()
    369 			if callee == nil {
    370 				// We don't know which function is being called.
    371 				s.set(v, ValueNilness{MaybeNil, MaybeNil})
    372 				return
    373 			}
    374 			calleeNilness := impl(pass, callee, seenFns)
    375 			if len(calleeNilness) > idx {
    376 				s.set(v, normalize(calleeNilness[idx], typ))
    377 			} else {
    378 				s.set(v, ValueNilness{MaybeNil, MaybeNil})
    379 			}
    380 		}
    381 
    382 		for _, instr := range from.Instrs {
    383 			// It is tempting to return early when instr is an ir.Value that
    384 			// doesn't have pointer type. However, instructions like ir.Load
    385 			// tell us something about the value being operated on.
    386 
    387 			switch v := instr.(type) {
    388 			case *ir.Convert:
    389 				s.set(v, s.get(v.X))
    390 			case *ir.SliceToArrayPointer:
    391 				// Go does not currently allow (*T)(s) where T is a type
    392 				// parameter with a type set consisting of array types, but it
    393 				// does allow (T)(s) where T is a type parameter with a type
    394 				// set consisting of pointers to array types.
    395 
    396 				allNonZero := typeutil.All(v.Type(), func(term *types.Term) bool {
    397 					ptr := term.Type().Underlying().(*types.Pointer).Elem()
    398 					return typeutil.All(ptr, func(innerTerm *types.Term) bool {
    399 						return innerTerm.Type().Underlying().(*types.Array).Len() != 0
    400 					})
    401 				})
    402 
    403 				if allNonZero {
    404 					// converting a slice to an array pointer of length > 0
    405 					// panics if the slice is nil
    406 					s.setOuter(v, NeverNil)
    407 					s.setOuter(v.X, NeverNil)
    408 				} else {
    409 					s.set(v, s.get(v.X))
    410 				}
    411 			case *ir.SliceToArray:
    412 				// Pretty much the same logic as SliceToArrayPointer, minus the
    413 				// pointer.
    414 
    415 				allNonZero := typeutil.All(v.Type(), func(term *types.Term) bool {
    416 					return term.Type().Underlying().(*types.Array).Len() != 0
    417 				})
    418 
    419 				if allNonZero {
    420 					// converting a slice to an array of length > 0
    421 					// panics if the slice is nil
    422 					s.setOuter(v.X, NeverNil)
    423 				}
    424 			case *ir.Slice:
    425 				if typeutil.All(v.X.Type(), typeutil.IsType[*types.Array]) {
    426 					// Slicing arrays never results in a nil slice.
    427 					s.setOuter(v, NeverNil)
    428 					continue
    429 				}
    430 
    431 				// checkBound returns true if one of the bounds (low, high,
    432 				// capacity) has non-zero value.
    433 				checkBound := func(v ir.Value) bool {
    434 					if v == nil {
    435 						return false
    436 					}
    437 					// TODO(dh): this is where integration with constant
    438 					// propagation and value range analysis would be useful.
    439 					if k, ok := v.(*ir.Const); ok {
    440 						kv, ok := constant.Int64Val(k.Value)
    441 						return !ok || kv != 0
    442 					}
    443 					return false
    444 				}
    445 				if checkBound(v.Low) || checkBound(v.High) || checkBound(v.Max) {
    446 					// One of the indices is non-zero, which means slicing can
    447 					// only succeed if the slicee is not nil.
    448 					s.setOuter(v, NeverNil)
    449 					s.setOuter(v.X, NeverNil)
    450 				} else {
    451 					// The new slice is as nilly as the slicee.
    452 					s.set(v, s.get(v.X))
    453 				}
    454 
    455 			case *ir.If:
    456 				cond := v.Cond
    457 				binop, ok := cond.(*ir.BinOp)
    458 				if !ok {
    459 					continue
    460 				}
    461 				isNil := func(v ir.Value) bool {
    462 					k, ok := v.(*ir.Const)
    463 					if !ok {
    464 						return false
    465 					}
    466 					return k.Value == nil
    467 				}
    468 				var target ir.Value
    469 				if isNil(binop.X) {
    470 					target = binop.Y
    471 				} else if isNil(binop.Y) {
    472 					target = binop.X
    473 				} else {
    474 					continue
    475 				}
    476 				op := binop.Op
    477 				if to != from.Succs[0] {
    478 					// we're in the false branch, negate op
    479 					switch op {
    480 					case token.EQL:
    481 						op = token.NEQ
    482 					case token.NEQ:
    483 						op = token.EQL
    484 					default:
    485 						panic(fmt.Sprintf("internal error: unhandled token %v", op))
    486 					}
    487 				}
    488 				switch op {
    489 				case token.EQL:
    490 					s.set(target, ValueNilness{AlwaysNil, AlwaysNil})
    491 				case token.NEQ:
    492 					s.setOuter(target, NeverNil)
    493 				default:
    494 					panic(fmt.Sprintf("internal error: unhandled token %v", op))
    495 				}
    496 
    497 				// TODO(dh): also handle comparison of two non-nil values. The
    498 				// true branch of neverNil == nilly makes the nilly value neverNil.
    499 			case *ir.ChangeType:
    500 				s.set(v, s.get(v.X))
    501 			case *ir.MultiConvert:
    502 				s.set(v, s.get(v.X))
    503 			case *ir.Load:
    504 				if _, ok := v.X.(*ir.Global); ok {
    505 					s.setOuter(v, MaybeNilGlobal)
    506 				} else {
    507 					s.setOuter(v, MaybeNil)
    508 				}
    509 				s.setOuter(v.X, NeverNil)
    510 			case *ir.FieldAddr:
    511 				s.setOuter(v.X, NeverNil)
    512 				s.setOuter(v, NeverNil)
    513 			case *ir.IndexAddr:
    514 				s.setOuter(v.X, NeverNil)
    515 				s.setOuter(v, NeverNil)
    516 			case *ir.Alloc, *ir.MakeMap, *ir.MakeSlice, *ir.MakeClosure, *ir.MakeChan:
    517 				s.setOuter(v.(ir.Value), NeverNil)
    518 			case *ir.MapUpdate:
    519 				s.setOuter(v.Map, NeverNil)
    520 			case *ir.Store:
    521 				s.setOuter(v.Addr, NeverNil)
    522 			case ir.CallInstruction:
    523 				// go/defer/calling a nil function fatals/panics
    524 				if !v.Common().IsInvoke() {
    525 					s.setOuter(v.Common().Value, NeverNil)
    526 				}
    527 				_, ok := v.(*ir.Call)
    528 				if !ok {
    529 					// Defer and Go don't produce values
    530 					continue
    531 				}
    532 				if v.Common().Signature().Results().Len() != 1 {
    533 					// If the called function doesn't return any values then we
    534 					// don't care about it. If it has more than one return
    535 					// value, they'll be handled by Extract.
    536 					continue
    537 				}
    538 
    539 				handleReturnValue(v.(ir.Value), v.(*ir.Call), 0)
    540 			case *ir.Send:
    541 				s.setOuter(v.Chan, NeverNil)
    542 			case *ir.Recv:
    543 				s.setOuter(v.Chan, NeverNil)
    544 				s.set(v, ValueNilness{MaybeNil, MaybeNil})
    545 			case *ir.MakeInterface:
    546 				s.set(v, ValueNilness{
    547 					Inner: s.get(v.X).Outer,
    548 					Outer: NeverNil,
    549 				})
    550 			case *ir.ChangeInterface:
    551 				s.set(v, s.get(v.X))
    552 			case *ir.TypeAssert:
    553 				if !v.CommaOk {
    554 					// The interface value cannot have been nil, or the type
    555 					// assertion would have panicked.
    556 					s.setOuter(v.X, NeverNil)
    557 
    558 					if types.IsInterface(v.Type()) && !typeparams.IsTypeParam(v.Type()) {
    559 						// Type asserting to another interface doesn't succeed
    560 						// if the assertee was nil. It also results in a new
    561 						// interface value.
    562 						s.setOuter(v, NeverNil)
    563 						s.setInner(v, s.get(v.X).Inner)
    564 					} else {
    565 						// We've extracted the interface value's inner value.
    566 						s.setOuter(v, s.get(v.X).Inner)
    567 					}
    568 				} else {
    569 					// In a comma-ok type assertion, the return type is a
    570 					// tuple. There'll be Extract instructions getting the
    571 					// individual values, to which we'll attach the nilness
    572 					// info.
    573 				}
    574 			case *ir.TypeSwitch:
    575 				// Handled in Extract
    576 			case *ir.MapLookup:
    577 				if s.get(v.X).Outer == AlwaysNil {
    578 					s.set(v, ValueNilness{AlwaysNil, AlwaysNil})
    579 				} else {
    580 					s.set(v, ValueNilness{MaybeNil, MaybeNil})
    581 				}
    582 			case *ir.Field:
    583 				s.set(v.X, ValueNilness{NeverNil, NeverNil})
    584 				s.set(v, ValueNilness{MaybeNil, MaybeNil})
    585 			case *ir.Index:
    586 				s.set(v.X, ValueNilness{NeverNil, NeverNil})
    587 				s.set(v, ValueNilness{MaybeNil, MaybeNil})
    588 
    589 			case *ir.Extract:
    590 				switch tuple := v.Tuple.(type) {
    591 				case *ir.TypeAssert:
    592 					// When we get here, the type assertion used the comma-ok
    593 					// form, and we don't yet know anything about the result of
    594 					// the type assertion.
    595 					if v.Index == 0 {
    596 						s.set(v, ValueNilness{MaybeNil, MaybeNil})
    597 					}
    598 
    599 					// TODO(dh): We should set v's nilness in the true and
    600 					// false branches of checks on the ok value. However, ok can be
    601 					// used in arbitrary ways, and we're also not set up to handle
    602 					// relational facts (ok being true or false affects the value
    603 					// of the other Extract).
    604 
    605 				case *ir.Call:
    606 					handleReturnValue(v, tuple, v.Index)
    607 
    608 				case *ir.TypeSwitch:
    609 					if v.Index == 0 {
    610 						// Index 0 is an integer and not interesting.
    611 						continue
    612 					}
    613 					idx := v.Index - 1
    614 					if idx >= len(tuple.Conds) {
    615 						// Default branch
    616 
    617 						// If there is an untyped nil case, then being in the
    618 						// default branch tells us that the interface value
    619 						// isn't nil.
    620 						hasNil := slices.ContainsFunc(tuple.Conds, func(typ types.Type) bool {
    621 							if typ, ok := typ.(*types.Basic); ok && typ.Kind() == types.UntypedNil {
    622 								return true
    623 							}
    624 							return false
    625 						})
    626 						if hasNil {
    627 							s.setOuter(tuple.Tag, NeverNil)
    628 						} else {
    629 							s.setOuter(tuple.Tag, MaybeNil)
    630 						}
    631 						s.setOuter(v, s.get(tuple.Tag).Inner)
    632 					} else {
    633 						// There is no Extract for the 'untyped nil' case,
    634 						// which means that executing any Extract from a type
    635 						// switch implies that the switched-over value wasn't a
    636 						// nil interface value.
    637 						s.setOuter(tuple.Tag, NeverNil)
    638 						typ := tuple.Conds[idx]
    639 						if types.IsInterface(typ) && !typeparams.IsTypeParam(typ) {
    640 							// Succesfully type asserting to an interface type
    641 							// always produces a non-nil interface value.
    642 							s.setInner(v, s.get(tuple.Tag).Inner)
    643 							s.setOuter(v, NeverNil)
    644 						} else {
    645 							s.setOuter(v, s.get(tuple.Tag).Inner)
    646 						}
    647 					}
    648 				default:
    649 					s.set(v, ValueNilness{MaybeNil, MaybeNil})
    650 				}
    651 			case *ir.Select:
    652 				if v.Blocking && len(v.States) == 1 {
    653 					// If the select doesn't have a default branch and only has
    654 					// one state, that state's channel cannot have been nil if
    655 					// we finished execution the select.
    656 					s.setOuter(v.States[0].Chan, NeverNil)
    657 				}
    658 			case *ir.Jump, *ir.BlankStore, *ir.Phi,
    659 				*ir.Panic, *ir.Return, *ir.RunDefers, *ir.Unreachable, *ir.ConstantSwitch,
    660 				*ir.UnOp, *ir.BinOp, *ir.CompositeValue, *ir.Range, *ir.Next:
    661 			default:
    662 				posn := pass.Fset.PositionFor(v.Pos(), false)
    663 				panic(fmt.Sprintf("internal error: unhandled type %T at %s", v, posn))
    664 			}
    665 		}
    666 		return s
    667 	}
    668 
    669 	processPhis := func(b *ir.BasicBlock, i int, s state) state {
    670 		for _, instr := range b.Instrs {
    671 			if instr, ok := instr.(*ir.Phi); ok {
    672 				s.set(instr, s.get(instr.Edges[i]))
    673 			} else {
    674 				break
    675 			}
    676 		}
    677 		return s
    678 	}
    679 
    680 	// Populate default state for non-instruction values we encounter. We
    681 	// cannot defer this logic to state.get because control flow merges use
    682 	// simple merges of lattice values and won't know about value-specific
    683 	// defaults.
    684 	entrys := state{cloned: true, n: n}
    685 	for _, param := range fn.Params {
    686 		if typeutil.IsPointerLike(param.Type()) {
    687 			entrys.set(param, ValueNilness{Inner: MaybeNil, Outer: MaybeNil})
    688 		} else {
    689 			// We never track nilness for value types, so they don't have to be
    690 			// present in the entry state, either.
    691 		}
    692 	}
    693 	if strings.HasPrefix(fn.Synthetic, "bound method wrapper") {
    694 		// This is a bound method and the bound receiver might be nil
    695 		for _, fvar := range fn.FreeVars {
    696 			entrys.set(fvar, ValueNilness{Outer: MaybeNil})
    697 		}
    698 	} else {
    699 		// This is a closure, and closed over variables are allocs, which
    700 		// cannot be nil.
    701 		for _, fvar := range fn.FreeVars {
    702 			entrys.set(fvar, ValueNilness{Outer: NeverNil})
    703 		}
    704 	}
    705 	var ops []*ir.Value
    706 	for _, b := range fn.Blocks {
    707 		for _, instr := range b.Instrs {
    708 			ops = instr.Operands(ops[:0])
    709 			for _, pop := range ops {
    710 				if op, ok := (*pop).(*ir.Const); ok && typeutil.IsPointerLike(op.Type()) {
    711 					// The only constant pointer-like is nil.
    712 					entrys.set(op, ValueNilness{Inner: AlwaysNil, Outer: AlwaysNil})
    713 				}
    714 			}
    715 		}
    716 	}
    717 	res := dense.Forward[dfa.DenseMapLattice[ValueNilness, lattice]](
    718 		fn,
    719 		map[int][]ValueNilness{0: entrys.m},
    720 		func(fromID, toID int, in []ValueNilness) []ValueNilness {
    721 			from := fn.Blocks[fromID]
    722 			to := fn.Blocks[toID]
    723 			s := state{n: n, m: in}
    724 			s = processBlock(from, to, s)
    725 			i := slices.Index(to.Preds, from)
    726 			s = processPhis(to, i, s)
    727 			return s.m
    728 		},
    729 	)
    730 
    731 	retNilness := make([]ValueNilness, fn.Signature.Results().Len())
    732 	for b := range fn.Returns() {
    733 		ret := b.Control().(*ir.Return)
    734 		s := state{n: n, m: res.In(b.Index)}
    735 		s = processBlock(b, nil, s)
    736 		for i, res := range ret.Results {
    737 			retNilness[i] = lattice{}.Merge(retNilness[i], s.get(res))
    738 		}
    739 	}
    740 
    741 	interesting := false
    742 	for i := range retNilness {
    743 		typ := fn.Signature.Results().At(i).Type()
    744 		if !typeutil.IsPointerLike(typ) {
    745 			retNilness[i] = ValueNilness{NeverNil, NeverNil}
    746 			continue
    747 		}
    748 		retNilness[i] = normalize(retNilness[i], typ)
    749 		if retNilness[i] != (ValueNilness{MaybeNil, MaybeNil}) {
    750 			interesting = true
    751 		}
    752 	}
    753 
    754 	if interesting {
    755 		pass.ExportObjectFact(fn.Object(), &nilnessFact{retNilness})
    756 	}
    757 
    758 	return retNilness
    759 }
    760 
    761 type lattice struct{}
    762 
    763 var _ dfa.Semilattice[ValueNilness] = lattice{}
    764 
    765 // Equals implements [dfa.Semilattice].
    766 func (l lattice) Equals(a, b ValueNilness) bool {
    767 	return a == b
    768 }
    769 
    770 // Ident implements [dfa.Semilattice].
    771 func (l lattice) Ident() ValueNilness {
    772 	return ValueNilness{}
    773 }
    774 
    775 var latticeMerge = [5][5]Nilness{
    776 	0: {
    777 		0:              0,
    778 		NeverNil:       NeverNil,
    779 		AlwaysNil:      AlwaysNil,
    780 		MaybeNilGlobal: MaybeNilGlobal,
    781 		MaybeNil:       MaybeNil,
    782 	},
    783 	NeverNil: {
    784 		0:              NeverNil,
    785 		NeverNil:       NeverNil,
    786 		AlwaysNil:      MaybeNil,
    787 		MaybeNilGlobal: MaybeNilGlobal,
    788 		MaybeNil:       MaybeNil,
    789 	},
    790 	AlwaysNil: {
    791 		0:              AlwaysNil,
    792 		NeverNil:       MaybeNil,
    793 		AlwaysNil:      AlwaysNil,
    794 		MaybeNilGlobal: MaybeNil,
    795 		MaybeNil:       MaybeNil,
    796 	},
    797 	MaybeNilGlobal: {
    798 		0:              MaybeNilGlobal,
    799 		NeverNil:       MaybeNilGlobal,
    800 		AlwaysNil:      MaybeNil,
    801 		MaybeNilGlobal: MaybeNilGlobal,
    802 		MaybeNil:       MaybeNil,
    803 	},
    804 	MaybeNil: {
    805 		0:              MaybeNil,
    806 		NeverNil:       MaybeNil,
    807 		AlwaysNil:      MaybeNil,
    808 		MaybeNilGlobal: MaybeNil,
    809 		MaybeNil:       MaybeNil,
    810 	},
    811 }
    812 
    813 // Merge implements [dfa.Semilattice].
    814 func (l lattice) Merge(a, b ValueNilness) ValueNilness {
    815 	return ValueNilness{
    816 		Inner: latticeMerge[a.Inner][b.Inner],
    817 		Outer: latticeMerge[a.Outer][b.Outer],
    818 	}
    819 }
    820 
    821 type numbering map[ir.Value]int
    822 
    823 func (n numbering) number(v ir.Value) int {
    824 	i, ok := n[v]
    825 	if !ok {
    826 		i = len(n)
    827 		n[v] = i
    828 	}
    829 	return i
    830 }