github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types/fmt.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package types 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "strconv" 12 "sync" 13 14 "github.com/go-asm/go/cmd/compile/base" 15 "github.com/go-asm/go/cmd/notsha256" 16 ) 17 18 // BuiltinPkg is a fake package that declares the universe block. 19 var BuiltinPkg *Pkg 20 21 // LocalPkg is the package being compiled. 22 var LocalPkg *Pkg 23 24 // UnsafePkg is package unsafe. 25 var UnsafePkg *Pkg 26 27 // BlankSym is the blank (_) symbol. 28 var BlankSym *Sym 29 30 // numImport tracks how often a package with a given name is imported. 31 // It is used to provide a better error message (by using the package 32 // path to disambiguate) if a package that appears multiple times with 33 // the same name appears in an error message. 34 var NumImport = make(map[string]int) 35 36 // fmtMode represents the kind of printing being done. 37 // The default is regular Go syntax (fmtGo). 38 // fmtDebug is like fmtGo but for debugging dumps and prints the type kind too. 39 // fmtTypeID and fmtTypeIDName are for generating various unique representations 40 // of types used in hashes, the linker, and function/method instantiations. 41 type fmtMode int 42 43 const ( 44 fmtGo fmtMode = iota 45 fmtDebug 46 fmtTypeID 47 fmtTypeIDName 48 ) 49 50 // Sym 51 52 // Format implements formatting for a Sym. 53 // The valid formats are: 54 // 55 // %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols. 56 // %+v Debug syntax: always include PkgName. prefix even for local names. 57 // %S Short syntax: Name only, no matter what. 58 func (s *Sym) Format(f fmt.State, verb rune) { 59 mode := fmtGo 60 switch verb { 61 case 'v', 'S': 62 if verb == 'v' && f.Flag('+') { 63 mode = fmtDebug 64 } 65 fmt.Fprint(f, sconv(s, verb, mode)) 66 67 default: 68 fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s) 69 } 70 } 71 72 func (s *Sym) String() string { 73 return sconv(s, 0, fmtGo) 74 } 75 76 // See #16897 for details about performance implications 77 // before changing the implementation of sconv. 78 func sconv(s *Sym, verb rune, mode fmtMode) string { 79 if verb == 'L' { 80 panic("linksymfmt") 81 } 82 83 if s == nil { 84 return "<S>" 85 } 86 87 q := pkgqual(s.Pkg, verb, mode) 88 if q == "" { 89 return s.Name 90 } 91 92 buf := fmtBufferPool.Get().(*bytes.Buffer) 93 buf.Reset() 94 defer fmtBufferPool.Put(buf) 95 96 buf.WriteString(q) 97 buf.WriteByte('.') 98 buf.WriteString(s.Name) 99 return InternString(buf.Bytes()) 100 } 101 102 func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { 103 if verb == 'L' { 104 panic("linksymfmt") 105 } 106 if s == nil { 107 b.WriteString("<S>") 108 return 109 } 110 111 symfmt(b, s, verb, mode) 112 } 113 114 func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { 115 name := s.Name 116 if q := pkgqual(s.Pkg, verb, mode); q != "" { 117 b.WriteString(q) 118 b.WriteByte('.') 119 } 120 b.WriteString(name) 121 } 122 123 // pkgqual returns the qualifier that should be used for printing 124 // symbols from the given package in the given mode. 125 // If it returns the empty string, no qualification is needed. 126 func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string { 127 if pkg == nil { 128 return "" 129 } 130 if verb != 'S' { 131 switch mode { 132 case fmtGo: // This is for the user 133 if pkg == BuiltinPkg || pkg == LocalPkg { 134 return "" 135 } 136 137 // If the name was used by multiple packages, display the full path, 138 if pkg.Name != "" && NumImport[pkg.Name] > 1 { 139 return strconv.Quote(pkg.Path) 140 } 141 return pkg.Name 142 143 case fmtDebug: 144 return pkg.Name 145 146 case fmtTypeIDName: 147 // dcommontype, typehash 148 return pkg.Name 149 150 case fmtTypeID: 151 // (methodsym), typesym, weaksym 152 return pkg.Prefix 153 } 154 } 155 156 return "" 157 } 158 159 // Type 160 161 var BasicTypeNames = []string{ 162 TINT: "int", 163 TUINT: "uint", 164 TINT8: "int8", 165 TUINT8: "uint8", 166 TINT16: "int16", 167 TUINT16: "uint16", 168 TINT32: "int32", 169 TUINT32: "uint32", 170 TINT64: "int64", 171 TUINT64: "uint64", 172 TUINTPTR: "uintptr", 173 TFLOAT32: "float32", 174 TFLOAT64: "float64", 175 TCOMPLEX64: "complex64", 176 TCOMPLEX128: "complex128", 177 TBOOL: "bool", 178 TANY: "any", 179 TSTRING: "string", 180 TNIL: "nil", 181 TIDEAL: "untyped number", 182 TBLANK: "blank", 183 } 184 185 var fmtBufferPool = sync.Pool{ 186 New: func() interface{} { 187 return new(bytes.Buffer) 188 }, 189 } 190 191 // Format implements formatting for a Type. 192 // The valid formats are: 193 // 194 // %v Go syntax 195 // %+v Debug syntax: Go syntax with a KIND- prefix for all but builtins. 196 // %L Go syntax for underlying type if t is named 197 // %S short Go syntax: drop leading "func" in function type 198 // %-S special case for method receiver symbol 199 func (t *Type) Format(s fmt.State, verb rune) { 200 mode := fmtGo 201 switch verb { 202 case 'v', 'S', 'L': 203 if verb == 'v' && s.Flag('+') { // %+v is debug format 204 mode = fmtDebug 205 } 206 if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format 207 mode = fmtTypeID 208 } 209 fmt.Fprint(s, tconv(t, verb, mode)) 210 default: 211 fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t) 212 } 213 } 214 215 // String returns the Go syntax for the type t. 216 func (t *Type) String() string { 217 return tconv(t, 0, fmtGo) 218 } 219 220 // LinkString returns a string description of t, suitable for use in 221 // link symbols. 222 // 223 // The description corresponds to type identity. That is, for any pair 224 // of types t1 and t2, Identical(t1, t2) == (t1.LinkString() == 225 // t2.LinkString()) is true. Thus it's safe to use as a map key to 226 // implement a type-identity-keyed map. 227 func (t *Type) LinkString() string { 228 return tconv(t, 0, fmtTypeID) 229 } 230 231 // NameString generates a user-readable, mostly unique string 232 // description of t. NameString always returns the same description 233 // for identical types, even across compilation units. 234 // 235 // NameString qualifies identifiers by package name, so it has 236 // collisions when different packages share the same names and 237 // identifiers. It also does not distinguish function-scope defined 238 // types from package-scoped defined types or from each other. 239 func (t *Type) NameString() string { 240 return tconv(t, 0, fmtTypeIDName) 241 } 242 243 func tconv(t *Type, verb rune, mode fmtMode) string { 244 buf := fmtBufferPool.Get().(*bytes.Buffer) 245 buf.Reset() 246 defer fmtBufferPool.Put(buf) 247 248 tconv2(buf, t, verb, mode, nil) 249 return InternString(buf.Bytes()) 250 } 251 252 // tconv2 writes a string representation of t to b. 253 // flag and mode control exactly what is printed. 254 // Any types x that are already in the visited map get printed as @%d where %d=visited[x]. 255 // See #16897 before changing the implementation of tconv. 256 func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) { 257 if off, ok := visited[t]; ok { 258 // We've seen this type before, so we're trying to print it recursively. 259 // Print a reference to it instead. 260 fmt.Fprintf(b, "@%d", off) 261 return 262 } 263 if t == nil { 264 b.WriteString("<T>") 265 return 266 } 267 if t.Kind() == TSSA { 268 b.WriteString(t.extra.(string)) 269 return 270 } 271 if t.Kind() == TTUPLE { 272 b.WriteString(t.FieldType(0).String()) 273 b.WriteByte(',') 274 b.WriteString(t.FieldType(1).String()) 275 return 276 } 277 278 if t.Kind() == TRESULTS { 279 tys := t.extra.(*Results).Types 280 for i, et := range tys { 281 if i > 0 { 282 b.WriteByte(',') 283 } 284 b.WriteString(et.String()) 285 } 286 return 287 } 288 289 if t == AnyType || t == ByteType || t == RuneType { 290 // in %-T mode collapse predeclared aliases with their originals. 291 switch mode { 292 case fmtTypeIDName, fmtTypeID: 293 t = Types[t.Kind()] 294 default: 295 sconv2(b, t.Sym(), 'S', mode) 296 return 297 } 298 } 299 if t == ErrorType { 300 b.WriteString("error") 301 return 302 } 303 304 // Unless the 'L' flag was specified, if the type has a name, just print that name. 305 if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] { 306 // Default to 'v' if verb is invalid. 307 if verb != 'S' { 308 verb = 'v' 309 } 310 311 // In unified IR, function-scope defined types will have a ·N 312 // suffix embedded directly in their Name. Trim this off for 313 // non-fmtTypeID modes. 314 sym := t.Sym() 315 if mode != fmtTypeID { 316 base, _ := SplitVargenSuffix(sym.Name) 317 if len(base) < len(sym.Name) { 318 sym = &Sym{Pkg: sym.Pkg, Name: base} 319 } 320 } 321 sconv2(b, sym, verb, mode) 322 return 323 } 324 325 if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" { 326 var name string 327 switch t { 328 case UntypedBool: 329 name = "untyped bool" 330 case UntypedString: 331 name = "untyped string" 332 case UntypedInt: 333 name = "untyped int" 334 case UntypedRune: 335 name = "untyped rune" 336 case UntypedFloat: 337 name = "untyped float" 338 case UntypedComplex: 339 name = "untyped complex" 340 default: 341 name = BasicTypeNames[t.Kind()] 342 } 343 b.WriteString(name) 344 return 345 } 346 347 if mode == fmtDebug { 348 b.WriteString(t.Kind().String()) 349 b.WriteByte('-') 350 tconv2(b, t, 'v', fmtGo, visited) 351 return 352 } 353 354 // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't 355 // try to print it recursively. 356 // We record the offset in the result buffer where the type's text starts. This offset serves as a reference 357 // point for any later references to the same type. 358 // Note that we remove the type from the visited map as soon as the recursive call is done. 359 // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work, 360 // but I'd like to use the @ notation only when strictly necessary.) 361 if visited == nil { 362 visited = map[*Type]int{} 363 } 364 visited[t] = b.Len() 365 defer delete(visited, t) 366 367 switch t.Kind() { 368 case TPTR: 369 b.WriteByte('*') 370 switch mode { 371 case fmtTypeID, fmtTypeIDName: 372 if verb == 'S' { 373 tconv2(b, t.Elem(), 'S', mode, visited) 374 return 375 } 376 } 377 tconv2(b, t.Elem(), 'v', mode, visited) 378 379 case TARRAY: 380 b.WriteByte('[') 381 b.WriteString(strconv.FormatInt(t.NumElem(), 10)) 382 b.WriteByte(']') 383 tconv2(b, t.Elem(), 0, mode, visited) 384 385 case TSLICE: 386 b.WriteString("[]") 387 tconv2(b, t.Elem(), 0, mode, visited) 388 389 case TCHAN: 390 switch t.ChanDir() { 391 case Crecv: 392 b.WriteString("<-chan ") 393 tconv2(b, t.Elem(), 0, mode, visited) 394 case Csend: 395 b.WriteString("chan<- ") 396 tconv2(b, t.Elem(), 0, mode, visited) 397 default: 398 b.WriteString("chan ") 399 if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv { 400 b.WriteByte('(') 401 tconv2(b, t.Elem(), 0, mode, visited) 402 b.WriteByte(')') 403 } else { 404 tconv2(b, t.Elem(), 0, mode, visited) 405 } 406 } 407 408 case TMAP: 409 b.WriteString("map[") 410 tconv2(b, t.Key(), 0, mode, visited) 411 b.WriteByte(']') 412 tconv2(b, t.Elem(), 0, mode, visited) 413 414 case TINTER: 415 if t.IsEmptyInterface() { 416 b.WriteString("interface {}") 417 break 418 } 419 b.WriteString("interface {") 420 for i, f := range t.AllMethods() { 421 if i != 0 { 422 b.WriteByte(';') 423 } 424 b.WriteByte(' ') 425 switch { 426 case f.Sym == nil: 427 // Check first that a symbol is defined for this type. 428 // Wrong interface definitions may have types lacking a symbol. 429 break 430 case IsExported(f.Sym.Name): 431 sconv2(b, f.Sym, 'S', mode) 432 default: 433 if mode != fmtTypeIDName { 434 mode = fmtTypeID 435 } 436 sconv2(b, f.Sym, 'v', mode) 437 } 438 tconv2(b, f.Type, 'S', mode, visited) 439 } 440 if len(t.AllMethods()) != 0 { 441 b.WriteByte(' ') 442 } 443 b.WriteByte('}') 444 445 case TFUNC: 446 if verb == 'S' { 447 // no leading func 448 } else { 449 if t.Recv() != nil { 450 b.WriteString("method") 451 formatParams(b, t.Recvs(), mode, visited) 452 b.WriteByte(' ') 453 } 454 b.WriteString("func") 455 } 456 formatParams(b, t.Params(), mode, visited) 457 458 switch t.NumResults() { 459 case 0: 460 // nothing to do 461 462 case 1: 463 b.WriteByte(' ') 464 tconv2(b, t.Result(0).Type, 0, mode, visited) // struct->field->field's type 465 466 default: 467 b.WriteByte(' ') 468 formatParams(b, t.Results(), mode, visited) 469 } 470 471 case TSTRUCT: 472 if m := t.StructType().Map; m != nil { 473 mt := m.MapType() 474 // Format the bucket struct for map[x]y as map.bucket[x]y. 475 // This avoids a recursive print that generates very long names. 476 switch t { 477 case mt.Bucket: 478 b.WriteString("map.bucket[") 479 default: 480 base.Fatalf("unknown internal map type") 481 } 482 tconv2(b, m.Key(), 0, mode, visited) 483 b.WriteByte(']') 484 tconv2(b, m.Elem(), 0, mode, visited) 485 break 486 } 487 488 b.WriteString("struct {") 489 for i, f := range t.Fields() { 490 if i != 0 { 491 b.WriteByte(';') 492 } 493 b.WriteByte(' ') 494 fldconv(b, f, 'L', mode, visited, false) 495 } 496 if t.NumFields() != 0 { 497 b.WriteByte(' ') 498 } 499 b.WriteByte('}') 500 501 case TFORW: 502 b.WriteString("undefined") 503 if t.Sym() != nil { 504 b.WriteByte(' ') 505 sconv2(b, t.Sym(), 'v', mode) 506 } 507 508 case TUNSAFEPTR: 509 b.WriteString("unsafe.Pointer") 510 511 case Txxx: 512 b.WriteString("Txxx") 513 514 default: 515 // Don't know how to handle - fall back to detailed prints 516 b.WriteString(t.Kind().String()) 517 b.WriteString(" <") 518 sconv2(b, t.Sym(), 'v', mode) 519 b.WriteString(">") 520 521 } 522 } 523 524 func formatParams(b *bytes.Buffer, params []*Field, mode fmtMode, visited map[*Type]int) { 525 b.WriteByte('(') 526 fieldVerb := 'v' 527 switch mode { 528 case fmtTypeID, fmtTypeIDName, fmtGo: 529 // no argument names on function signature, and no "noescape"/"nosplit" tags 530 fieldVerb = 'S' 531 } 532 for i, param := range params { 533 if i != 0 { 534 b.WriteString(", ") 535 } 536 fldconv(b, param, fieldVerb, mode, visited, true) 537 } 538 b.WriteByte(')') 539 } 540 541 func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, isParam bool) { 542 if f == nil { 543 b.WriteString("<T>") 544 return 545 } 546 547 var name string 548 nameSep := " " 549 if verb != 'S' { 550 s := f.Sym 551 552 // Using type aliases and embedded fields, it's possible to 553 // construct types that can't be directly represented as a 554 // type literal. For example, given "type Int = int" (#50190), 555 // it would be incorrect to format "struct{ Int }" as either 556 // "struct{ int }" or "struct{ Int int }", because those each 557 // represent other, distinct types. 558 // 559 // So for the purpose of LinkString (i.e., fmtTypeID), we use 560 // the non-standard syntax "struct{ Int = int }" to represent 561 // embedded fields that have been renamed through the use of 562 // type aliases. 563 if f.Embedded != 0 { 564 if mode == fmtTypeID { 565 nameSep = " = " 566 567 // Compute tsym, the symbol that would normally be used as 568 // the field name when embedding f.Type. 569 // TODO(mdempsky): Check for other occurrences of this logic 570 // and deduplicate. 571 typ := f.Type 572 if typ.IsPtr() { 573 base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ) 574 typ = typ.Elem() 575 } 576 tsym := typ.Sym() 577 578 // If the field name matches the embedded type's name, then 579 // suppress printing of the field name. For example, format 580 // "struct{ T }" as simply that instead of "struct{ T = T }". 581 if tsym != nil && (s == tsym || IsExported(tsym.Name) && s.Name == tsym.Name) { 582 s = nil 583 } 584 } else { 585 // Suppress the field name for embedded fields for 586 // non-LinkString formats, to match historical behavior. 587 // TODO(mdempsky): Re-evaluate this. 588 s = nil 589 } 590 } 591 592 if s != nil { 593 if isParam { 594 name = fmt.Sprint(f.Nname) 595 } else if verb == 'L' { 596 name = s.Name 597 if !IsExported(name) && mode != fmtTypeIDName { 598 name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg) 599 } 600 } else { 601 name = sconv(s, 0, mode) 602 } 603 } 604 } 605 606 if name != "" { 607 b.WriteString(name) 608 b.WriteString(nameSep) 609 } 610 611 if f.IsDDD() { 612 var et *Type 613 if f.Type != nil { 614 et = f.Type.Elem() 615 } 616 b.WriteString("...") 617 tconv2(b, et, 0, mode, visited) 618 } else { 619 tconv2(b, f.Type, 0, mode, visited) 620 } 621 622 if verb != 'S' && !isParam && f.Note != "" { 623 b.WriteString(" ") 624 b.WriteString(strconv.Quote(f.Note)) 625 } 626 } 627 628 // SplitVargenSuffix returns name split into a base string and a ·N 629 // suffix, if any. 630 func SplitVargenSuffix(name string) (base, suffix string) { 631 i := len(name) 632 for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { 633 i-- 634 } 635 const dot = "·" 636 if i >= len(dot) && name[i-len(dot):i] == dot { 637 i -= len(dot) 638 return name[:i], name[i:] 639 } 640 return name, "" 641 } 642 643 // TypeHash computes a hash value for type t to use in type switch statements. 644 func TypeHash(t *Type) uint32 { 645 p := t.LinkString() 646 647 // Using SHA256 is overkill, but reduces accidental collisions. 648 h := notsha256.Sum256([]byte(p)) 649 return binary.LittleEndian.Uint32(h[:4]) 650 }