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