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 }