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.
15 "cmd/compile/internal/base"
16 "cmd/internal/notsha256"
19 // BuiltinPkg is a fake package that declares the universe block.
22 // LocalPkg is the package being compiled.
25 // UnsafePkg is package unsafe.
28 // BlankSym is the blank (_) symbol.
31 // OrigSym returns the original symbol written by the user.
32 func OrigSym(s *Sym) *Sym {
37 if len(s.Name) > 1 && s.Name[0] == '~' {
39 case 'r': // originally an unnamed result
41 case 'b': // originally the blank identifier _
42 // TODO(mdempsky): Does s.Pkg matter here?
48 if strings.HasPrefix(s.Name, ".anon") {
49 // originally an unnamed or _ name (see subr.go: NewFuncParams)
56 // numImport tracks how often a package with a given name is imported.
57 // It is used to provide a better error message (by using the package
58 // path to disambiguate) if a package that appears multiple times with
59 // the same name appears in an error message.
60 var NumImport = make(map[string]int)
62 // fmtMode represents the kind of printing being done.
63 // The default is regular Go syntax (fmtGo).
64 // fmtDebug is like fmtGo but for debugging dumps and prints the type kind too.
65 // fmtTypeID and fmtTypeIDName are for generating various unique representations
66 // of types used in hashes, the linker, and function/method instantiations.
78 // Format implements formatting for a Sym.
79 // The valid formats are:
81 // %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols.
82 // %+v Debug syntax: always include PkgName. prefix even for local names.
83 // %S Short syntax: Name only, no matter what.
84 func (s *Sym) Format(f fmt.State, verb rune) {
88 if verb == 'v' && f.Flag('+') {
91 fmt.Fprint(f, sconv(s, verb, mode))
94 fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
98 func (s *Sym) String() string {
99 return sconv(s, 0, fmtGo)
102 // See #16897 for details about performance implications
103 // before changing the implementation of sconv.
104 func sconv(s *Sym, verb rune, mode fmtMode) string {
113 q := pkgqual(s.Pkg, verb, mode)
118 buf := fmtBufferPool.Get().(*bytes.Buffer)
120 defer fmtBufferPool.Put(buf)
124 buf.WriteString(s.Name)
125 return InternString(buf.Bytes())
128 func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
137 symfmt(b, s, verb, mode)
140 func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
142 if q := pkgqual(s.Pkg, verb, mode); q != "" {
149 // pkgqual returns the qualifier that should be used for printing
150 // symbols from the given package in the given mode.
151 // If it returns the empty string, no qualification is needed.
152 func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
158 case fmtGo: // This is for the user
159 if pkg == BuiltinPkg || pkg == LocalPkg {
163 // If the name was used by multiple packages, display the full path,
164 if pkg.Name != "" && NumImport[pkg.Name] > 1 {
165 return strconv.Quote(pkg.Path)
173 // dcommontype, typehash
177 // (methodsym), typesym, weaksym
187 var BasicTypeNames = []string{
201 TCOMPLEX64: "complex64",
202 TCOMPLEX128: "complex128",
207 TIDEAL: "untyped number",
211 var fmtBufferPool = sync.Pool{
212 New: func() interface{} {
213 return new(bytes.Buffer)
217 // Format implements formatting for a Type.
218 // The valid formats are:
221 // %+v Debug syntax: Go syntax with a KIND- prefix for all but builtins.
222 // %L Go syntax for underlying type if t is named
223 // %S short Go syntax: drop leading "func" in function type
224 // %-S special case for method receiver symbol
225 func (t *Type) Format(s fmt.State, verb rune) {
229 if verb == 'v' && s.Flag('+') { // %+v is debug format
232 if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format
235 fmt.Fprint(s, tconv(t, verb, mode))
237 fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
241 // String returns the Go syntax for the type t.
242 func (t *Type) String() string {
243 return tconv(t, 0, fmtGo)
246 // LinkString returns a string description of t, suitable for use in
249 // The description corresponds to type identity. That is, for any pair
250 // of types t1 and t2, Identical(t1, t2) == (t1.LinkString() ==
251 // t2.LinkString()) is true. Thus it's safe to use as a map key to
252 // implement a type-identity-keyed map.
253 func (t *Type) LinkString() string {
254 return tconv(t, 0, fmtTypeID)
257 // NameString generates a user-readable, mostly unique string
258 // description of t. NameString always returns the same description
259 // for identical types, even across compilation units.
261 // NameString qualifies identifiers by package name, so it has
262 // collisions when different packages share the same names and
263 // identifiers. It also does not distinguish function-scope defined
264 // types from package-scoped defined types or from each other.
265 func (t *Type) NameString() string {
266 return tconv(t, 0, fmtTypeIDName)
269 func tconv(t *Type, verb rune, mode fmtMode) string {
270 buf := fmtBufferPool.Get().(*bytes.Buffer)
272 defer fmtBufferPool.Put(buf)
274 tconv2(buf, t, verb, mode, nil)
275 return InternString(buf.Bytes())
278 // tconv2 writes a string representation of t to b.
279 // flag and mode control exactly what is printed.
280 // Any types x that are already in the visited map get printed as @%d where %d=visited[x].
281 // See #16897 before changing the implementation of tconv.
282 func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) {
283 if off, ok := visited[t]; ok {
284 // We've seen this type before, so we're trying to print it recursively.
285 // Print a reference to it instead.
286 fmt.Fprintf(b, "@%d", off)
293 if t.Kind() == TSSA {
294 b.WriteString(t.extra.(string))
297 if t.Kind() == TTUPLE {
298 b.WriteString(t.FieldType(0).String())
300 b.WriteString(t.FieldType(1).String())
304 if t.Kind() == TRESULTS {
305 tys := t.extra.(*Results).Types
306 for i, et := range tys {
310 b.WriteString(et.String())
315 if t == AnyType || t == ByteType || t == RuneType {
316 // in %-T mode collapse predeclared aliases with their originals.
318 case fmtTypeIDName, fmtTypeID:
321 sconv2(b, t.Sym(), 'S', mode)
326 b.WriteString("error")
330 // Unless the 'L' flag was specified, if the type has a name, just print that name.
331 if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
332 // Default to 'v' if verb is invalid.
337 // In unified IR, function-scope defined types will have a ·N
338 // suffix embedded directly in their Name. Trim this off for
339 // non-fmtTypeID modes.
341 if mode != fmtTypeID {
342 base, _ := SplitVargenSuffix(sym.Name)
343 if len(base) < len(sym.Name) {
344 sym = &Sym{Pkg: sym.Pkg, Name: base}
347 sconv2(b, sym, verb, mode)
349 // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName
350 // output too. It seems like it should, but that mode is currently
351 // used in string representation used by reflection, which is
352 // user-visible and doesn't expect this.
353 if mode == fmtTypeID && t.vargen != 0 {
354 fmt.Fprintf(b, "·%d", t.vargen)
359 if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" {
363 name = "untyped bool"
365 name = "untyped string"
369 name = "untyped rune"
371 name = "untyped float"
373 name = "untyped complex"
375 name = BasicTypeNames[t.Kind()]
381 if mode == fmtDebug {
382 b.WriteString(t.Kind().String())
384 tconv2(b, t, 'v', fmtGo, visited)
388 // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
389 // try to print it recursively.
390 // We record the offset in the result buffer where the type's text starts. This offset serves as a reference
391 // point for any later references to the same type.
392 // Note that we remove the type from the visited map as soon as the recursive call is done.
393 // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work,
394 // but I'd like to use the @ notation only when strictly necessary.)
396 visited = map[*Type]int{}
399 defer delete(visited, t)
405 case fmtTypeID, fmtTypeIDName:
407 tconv2(b, t.Elem(), 'S', mode, visited)
411 tconv2(b, t.Elem(), 'v', mode, visited)
415 b.WriteString(strconv.FormatInt(t.NumElem(), 10))
417 tconv2(b, t.Elem(), 0, mode, visited)
421 tconv2(b, t.Elem(), 0, mode, visited)
426 b.WriteString("<-chan ")
427 tconv2(b, t.Elem(), 0, mode, visited)
429 b.WriteString("chan<- ")
430 tconv2(b, t.Elem(), 0, mode, visited)
432 b.WriteString("chan ")
433 if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv {
435 tconv2(b, t.Elem(), 0, mode, visited)
438 tconv2(b, t.Elem(), 0, mode, visited)
443 b.WriteString("map[")
444 tconv2(b, t.Key(), 0, mode, visited)
446 tconv2(b, t.Elem(), 0, mode, visited)
449 if t.IsEmptyInterface() {
450 b.WriteString("interface {}")
453 b.WriteString("interface {")
454 for i, f := range t.AllMethods() {
461 // Check first that a symbol is defined for this type.
462 // Wrong interface definitions may have types lacking a symbol.
464 case IsExported(f.Sym.Name):
465 sconv2(b, f.Sym, 'S', mode)
467 if mode != fmtTypeIDName {
470 sconv2(b, f.Sym, 'v', mode)
472 tconv2(b, f.Type, 'S', mode, visited)
474 if len(t.AllMethods()) != 0 {
484 b.WriteString("method")
485 formatParams(b, t.Recvs(), mode, visited)
488 b.WriteString("func")
490 formatParams(b, t.Params(), mode, visited)
492 switch t.NumResults() {
498 tconv2(b, t.Result(0).Type, 0, mode, visited) // struct->field->field's type
502 formatParams(b, t.Results(), mode, visited)
506 if m := t.StructType().Map; m != nil {
508 // Format the bucket struct for map[x]y as map.bucket[x]y.
509 // This avoids a recursive print that generates very long names.
512 b.WriteString("map.bucket[")
514 base.Fatalf("unknown internal map type")
516 tconv2(b, m.Key(), 0, mode, visited)
518 tconv2(b, m.Elem(), 0, mode, visited)
522 b.WriteString("struct {")
523 for i, f := range t.Fields() {
528 fldconv(b, f, 'L', mode, visited, false)
530 if t.NumFields() != 0 {
536 b.WriteString("undefined")
539 sconv2(b, t.Sym(), 'v', mode)
543 b.WriteString("unsafe.Pointer")
546 b.WriteString("Txxx")
549 // Don't know how to handle - fall back to detailed prints
550 b.WriteString(t.Kind().String())
552 sconv2(b, t.Sym(), 'v', mode)
558 func formatParams(b *bytes.Buffer, params []*Field, mode fmtMode, visited map[*Type]int) {
562 case fmtTypeID, fmtTypeIDName, fmtGo:
563 // no argument names on function signature, and no "noescape"/"nosplit" tags
566 for i, param := range params {
570 fldconv(b, param, fieldVerb, mode, visited, true)
575 func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, isParam bool) {
586 // Take the name from the original.
591 // Using type aliases and embedded fields, it's possible to
592 // construct types that can't be directly represented as a
593 // type literal. For example, given "type Int = int" (#50190),
594 // it would be incorrect to format "struct{ Int }" as either
595 // "struct{ int }" or "struct{ Int int }", because those each
596 // represent other, distinct types.
598 // So for the purpose of LinkString (i.e., fmtTypeID), we use
599 // the non-standard syntax "struct{ Int = int }" to represent
600 // embedded fields that have been renamed through the use of
603 if mode == fmtTypeID {
606 // Compute tsym, the symbol that would normally be used as
607 // the field name when embedding f.Type.
608 // TODO(mdempsky): Check for other occurrences of this logic
612 base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ)
617 // If the field name matches the embedded type's name, then
618 // suppress printing of the field name. For example, format
619 // "struct{ T }" as simply that instead of "struct{ T = T }".
620 if tsym != nil && (s == tsym || IsExported(tsym.Name) && s.Name == tsym.Name) {
624 // Suppress the field name for embedded fields for
625 // non-LinkString formats, to match historical behavior.
626 // TODO(mdempsky): Re-evaluate this.
633 name = fmt.Sprint(f.Nname)
634 } else if verb == 'L' {
636 if !IsExported(name) && mode != fmtTypeIDName {
637 name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
640 name = sconv(s, 0, mode)
647 b.WriteString(nameSep)
656 tconv2(b, et, 0, mode, visited)
658 tconv2(b, f.Type, 0, mode, visited)
661 if verb != 'S' && !isParam && f.Note != "" {
663 b.WriteString(strconv.Quote(f.Note))
667 // SplitVargenSuffix returns name split into a base string and a ·N
669 func SplitVargenSuffix(name string) (base, suffix string) {
671 for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
675 if i >= len(dot) && name[i-len(dot):i] == dot {
677 return name[:i], name[i:]
682 // TypeHash computes a hash value for type t to use in type switch statements.
683 func TypeHash(t *Type) uint32 {
686 // Using SHA256 is overkill, but reduces accidental collisions.
687 h := notsha256.Sum256([]byte(p))
688 return binary.LittleEndian.Uint32(h[:4])