]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types/fmt.go
cmd/compile/internal/typecheck: fix closure field naming
[gostls13.git] / src / cmd / compile / internal / types / fmt.go
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         "strings"
13         "sync"
14
15         "cmd/compile/internal/base"
16         "cmd/internal/notsha256"
17 )
18
19 // BuiltinPkg is a fake package that declares the universe block.
20 var BuiltinPkg *Pkg
21
22 // LocalPkg is the package being compiled.
23 var LocalPkg *Pkg
24
25 // UnsafePkg is package unsafe.
26 var UnsafePkg *Pkg
27
28 // BlankSym is the blank (_) symbol.
29 var BlankSym *Sym
30
31 // OrigSym returns the original symbol written by the user.
32 func OrigSym(s *Sym) *Sym {
33         if s == nil {
34                 return nil
35         }
36
37         if len(s.Name) > 1 && s.Name[0] == '~' {
38                 switch s.Name[1] {
39                 case 'r': // originally an unnamed result
40                         return nil
41                 case 'b': // originally the blank identifier _
42                         // TODO(mdempsky): Does s.Pkg matter here?
43                         return BlankSym
44                 }
45                 return s
46         }
47
48         if strings.HasPrefix(s.Name, ".anon") {
49                 // originally an unnamed or _ name (see subr.go: NewFuncParams)
50                 return nil
51         }
52
53         return s
54 }
55
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)
61
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.
67 type fmtMode int
68
69 const (
70         fmtGo fmtMode = iota
71         fmtDebug
72         fmtTypeID
73         fmtTypeIDName
74 )
75
76 // Sym
77
78 // Format implements formatting for a Sym.
79 // The valid formats are:
80 //
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) {
85         mode := fmtGo
86         switch verb {
87         case 'v', 'S':
88                 if verb == 'v' && f.Flag('+') {
89                         mode = fmtDebug
90                 }
91                 fmt.Fprint(f, sconv(s, verb, mode))
92
93         default:
94                 fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
95         }
96 }
97
98 func (s *Sym) String() string {
99         return sconv(s, 0, fmtGo)
100 }
101
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 {
105         if verb == 'L' {
106                 panic("linksymfmt")
107         }
108
109         if s == nil {
110                 return "<S>"
111         }
112
113         q := pkgqual(s.Pkg, verb, mode)
114         if q == "" {
115                 return s.Name
116         }
117
118         buf := fmtBufferPool.Get().(*bytes.Buffer)
119         buf.Reset()
120         defer fmtBufferPool.Put(buf)
121
122         buf.WriteString(q)
123         buf.WriteByte('.')
124         buf.WriteString(s.Name)
125         return InternString(buf.Bytes())
126 }
127
128 func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
129         if verb == 'L' {
130                 panic("linksymfmt")
131         }
132         if s == nil {
133                 b.WriteString("<S>")
134                 return
135         }
136
137         symfmt(b, s, verb, mode)
138 }
139
140 func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
141         name := s.Name
142         if q := pkgqual(s.Pkg, verb, mode); q != "" {
143                 b.WriteString(q)
144                 b.WriteByte('.')
145         }
146         b.WriteString(name)
147 }
148
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 {
153         if pkg == nil {
154                 return ""
155         }
156         if verb != 'S' {
157                 switch mode {
158                 case fmtGo: // This is for the user
159                         if pkg == BuiltinPkg || pkg == LocalPkg {
160                                 return ""
161                         }
162
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)
166                         }
167                         return pkg.Name
168
169                 case fmtDebug:
170                         return pkg.Name
171
172                 case fmtTypeIDName:
173                         // dcommontype, typehash
174                         return pkg.Name
175
176                 case fmtTypeID:
177                         // (methodsym), typesym, weaksym
178                         return pkg.Prefix
179                 }
180         }
181
182         return ""
183 }
184
185 // Type
186
187 var BasicTypeNames = []string{
188         TINT:        "int",
189         TUINT:       "uint",
190         TINT8:       "int8",
191         TUINT8:      "uint8",
192         TINT16:      "int16",
193         TUINT16:     "uint16",
194         TINT32:      "int32",
195         TUINT32:     "uint32",
196         TINT64:      "int64",
197         TUINT64:     "uint64",
198         TUINTPTR:    "uintptr",
199         TFLOAT32:    "float32",
200         TFLOAT64:    "float64",
201         TCOMPLEX64:  "complex64",
202         TCOMPLEX128: "complex128",
203         TBOOL:       "bool",
204         TANY:        "any",
205         TSTRING:     "string",
206         TNIL:        "nil",
207         TIDEAL:      "untyped number",
208         TBLANK:      "blank",
209 }
210
211 var fmtBufferPool = sync.Pool{
212         New: func() interface{} {
213                 return new(bytes.Buffer)
214         },
215 }
216
217 // Format implements formatting for a Type.
218 // The valid formats are:
219 //
220 //      %v      Go syntax
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) {
226         mode := fmtGo
227         switch verb {
228         case 'v', 'S', 'L':
229                 if verb == 'v' && s.Flag('+') { // %+v is debug format
230                         mode = fmtDebug
231                 }
232                 if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format
233                         mode = fmtTypeID
234                 }
235                 fmt.Fprint(s, tconv(t, verb, mode))
236         default:
237                 fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
238         }
239 }
240
241 // String returns the Go syntax for the type t.
242 func (t *Type) String() string {
243         return tconv(t, 0, fmtGo)
244 }
245
246 // LinkString returns a string description of t, suitable for use in
247 // link symbols.
248 //
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)
255 }
256
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.
260 //
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)
267 }
268
269 func tconv(t *Type, verb rune, mode fmtMode) string {
270         buf := fmtBufferPool.Get().(*bytes.Buffer)
271         buf.Reset()
272         defer fmtBufferPool.Put(buf)
273
274         tconv2(buf, t, verb, mode, nil)
275         return InternString(buf.Bytes())
276 }
277
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)
287                 return
288         }
289         if t == nil {
290                 b.WriteString("<T>")
291                 return
292         }
293         if t.Kind() == TSSA {
294                 b.WriteString(t.extra.(string))
295                 return
296         }
297         if t.Kind() == TTUPLE {
298                 b.WriteString(t.FieldType(0).String())
299                 b.WriteByte(',')
300                 b.WriteString(t.FieldType(1).String())
301                 return
302         }
303
304         if t.Kind() == TRESULTS {
305                 tys := t.extra.(*Results).Types
306                 for i, et := range tys {
307                         if i > 0 {
308                                 b.WriteByte(',')
309                         }
310                         b.WriteString(et.String())
311                 }
312                 return
313         }
314
315         if t == AnyType || t == ByteType || t == RuneType {
316                 // in %-T mode collapse predeclared aliases with their originals.
317                 switch mode {
318                 case fmtTypeIDName, fmtTypeID:
319                         t = Types[t.Kind()]
320                 default:
321                         sconv2(b, t.Sym(), 'S', mode)
322                         return
323                 }
324         }
325         if t == ErrorType {
326                 b.WriteString("error")
327                 return
328         }
329
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.
333                 if verb != 'S' {
334                         verb = 'v'
335                 }
336
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.
340                 sym := t.Sym()
341                 if mode != fmtTypeID {
342                         base, _ := SplitVargenSuffix(sym.Name)
343                         if len(base) < len(sym.Name) {
344                                 sym = &Sym{Pkg: sym.Pkg, Name: base}
345                         }
346                 }
347                 sconv2(b, sym, verb, mode)
348
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)
355                 }
356                 return
357         }
358
359         if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" {
360                 var name string
361                 switch t {
362                 case UntypedBool:
363                         name = "untyped bool"
364                 case UntypedString:
365                         name = "untyped string"
366                 case UntypedInt:
367                         name = "untyped int"
368                 case UntypedRune:
369                         name = "untyped rune"
370                 case UntypedFloat:
371                         name = "untyped float"
372                 case UntypedComplex:
373                         name = "untyped complex"
374                 default:
375                         name = BasicTypeNames[t.Kind()]
376                 }
377                 b.WriteString(name)
378                 return
379         }
380
381         if mode == fmtDebug {
382                 b.WriteString(t.Kind().String())
383                 b.WriteByte('-')
384                 tconv2(b, t, 'v', fmtGo, visited)
385                 return
386         }
387
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.)
395         if visited == nil {
396                 visited = map[*Type]int{}
397         }
398         visited[t] = b.Len()
399         defer delete(visited, t)
400
401         switch t.Kind() {
402         case TPTR:
403                 b.WriteByte('*')
404                 switch mode {
405                 case fmtTypeID, fmtTypeIDName:
406                         if verb == 'S' {
407                                 tconv2(b, t.Elem(), 'S', mode, visited)
408                                 return
409                         }
410                 }
411                 tconv2(b, t.Elem(), 'v', mode, visited)
412
413         case TARRAY:
414                 b.WriteByte('[')
415                 b.WriteString(strconv.FormatInt(t.NumElem(), 10))
416                 b.WriteByte(']')
417                 tconv2(b, t.Elem(), 0, mode, visited)
418
419         case TSLICE:
420                 b.WriteString("[]")
421                 tconv2(b, t.Elem(), 0, mode, visited)
422
423         case TCHAN:
424                 switch t.ChanDir() {
425                 case Crecv:
426                         b.WriteString("<-chan ")
427                         tconv2(b, t.Elem(), 0, mode, visited)
428                 case Csend:
429                         b.WriteString("chan<- ")
430                         tconv2(b, t.Elem(), 0, mode, visited)
431                 default:
432                         b.WriteString("chan ")
433                         if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv {
434                                 b.WriteByte('(')
435                                 tconv2(b, t.Elem(), 0, mode, visited)
436                                 b.WriteByte(')')
437                         } else {
438                                 tconv2(b, t.Elem(), 0, mode, visited)
439                         }
440                 }
441
442         case TMAP:
443                 b.WriteString("map[")
444                 tconv2(b, t.Key(), 0, mode, visited)
445                 b.WriteByte(']')
446                 tconv2(b, t.Elem(), 0, mode, visited)
447
448         case TINTER:
449                 if t.IsEmptyInterface() {
450                         b.WriteString("interface {}")
451                         break
452                 }
453                 b.WriteString("interface {")
454                 for i, f := range t.AllMethods() {
455                         if i != 0 {
456                                 b.WriteByte(';')
457                         }
458                         b.WriteByte(' ')
459                         switch {
460                         case f.Sym == nil:
461                                 // Check first that a symbol is defined for this type.
462                                 // Wrong interface definitions may have types lacking a symbol.
463                                 break
464                         case IsExported(f.Sym.Name):
465                                 sconv2(b, f.Sym, 'S', mode)
466                         default:
467                                 if mode != fmtTypeIDName {
468                                         mode = fmtTypeID
469                                 }
470                                 sconv2(b, f.Sym, 'v', mode)
471                         }
472                         tconv2(b, f.Type, 'S', mode, visited)
473                 }
474                 if len(t.AllMethods()) != 0 {
475                         b.WriteByte(' ')
476                 }
477                 b.WriteByte('}')
478
479         case TFUNC:
480                 if verb == 'S' {
481                         // no leading func
482                 } else {
483                         if t.Recv() != nil {
484                                 b.WriteString("method")
485                                 formatParams(b, t.Recvs(), mode, visited)
486                                 b.WriteByte(' ')
487                         }
488                         b.WriteString("func")
489                 }
490                 formatParams(b, t.Params(), mode, visited)
491
492                 switch t.NumResults() {
493                 case 0:
494                         // nothing to do
495
496                 case 1:
497                         b.WriteByte(' ')
498                         tconv2(b, t.Result(0).Type, 0, mode, visited) // struct->field->field's type
499
500                 default:
501                         b.WriteByte(' ')
502                         formatParams(b, t.Results(), mode, visited)
503                 }
504
505         case TSTRUCT:
506                 if m := t.StructType().Map; m != nil {
507                         mt := m.MapType()
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.
510                         switch t {
511                         case mt.Bucket:
512                                 b.WriteString("map.bucket[")
513                         default:
514                                 base.Fatalf("unknown internal map type")
515                         }
516                         tconv2(b, m.Key(), 0, mode, visited)
517                         b.WriteByte(']')
518                         tconv2(b, m.Elem(), 0, mode, visited)
519                         break
520                 }
521
522                 b.WriteString("struct {")
523                 for i, f := range t.Fields() {
524                         if i != 0 {
525                                 b.WriteByte(';')
526                         }
527                         b.WriteByte(' ')
528                         fldconv(b, f, 'L', mode, visited, false)
529                 }
530                 if t.NumFields() != 0 {
531                         b.WriteByte(' ')
532                 }
533                 b.WriteByte('}')
534
535         case TFORW:
536                 b.WriteString("undefined")
537                 if t.Sym() != nil {
538                         b.WriteByte(' ')
539                         sconv2(b, t.Sym(), 'v', mode)
540                 }
541
542         case TUNSAFEPTR:
543                 b.WriteString("unsafe.Pointer")
544
545         case Txxx:
546                 b.WriteString("Txxx")
547
548         default:
549                 // Don't know how to handle - fall back to detailed prints
550                 b.WriteString(t.Kind().String())
551                 b.WriteString(" <")
552                 sconv2(b, t.Sym(), 'v', mode)
553                 b.WriteString(">")
554
555         }
556 }
557
558 func formatParams(b *bytes.Buffer, params []*Field, mode fmtMode, visited map[*Type]int) {
559         b.WriteByte('(')
560         fieldVerb := 'v'
561         switch mode {
562         case fmtTypeID, fmtTypeIDName, fmtGo:
563                 // no argument names on function signature, and no "noescape"/"nosplit" tags
564                 fieldVerb = 'S'
565         }
566         for i, param := range params {
567                 if i != 0 {
568                         b.WriteString(", ")
569                 }
570                 fldconv(b, param, fieldVerb, mode, visited, true)
571         }
572         b.WriteByte(')')
573 }
574
575 func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, isParam bool) {
576         if f == nil {
577                 b.WriteString("<T>")
578                 return
579         }
580
581         var name string
582         nameSep := " "
583         if verb != 'S' {
584                 s := f.Sym
585
586                 // Take the name from the original.
587                 if mode == fmtGo {
588                         s = OrigSym(s)
589                 }
590
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.
597                 //
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
601                 // type aliases.
602                 if f.Embedded != 0 {
603                         if mode == fmtTypeID {
604                                 nameSep = " = "
605
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
609                                 // and deduplicate.
610                                 typ := f.Type
611                                 if typ.IsPtr() {
612                                         base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ)
613                                         typ = typ.Elem()
614                                 }
615                                 tsym := typ.Sym()
616
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) {
621                                         s = nil
622                                 }
623                         } else {
624                                 // Suppress the field name for embedded fields for
625                                 // non-LinkString formats, to match historical behavior.
626                                 // TODO(mdempsky): Re-evaluate this.
627                                 s = nil
628                         }
629                 }
630
631                 if s != nil {
632                         if isParam {
633                                 name = fmt.Sprint(f.Nname)
634                         } else if verb == 'L' {
635                                 name = s.Name
636                                 if !IsExported(name) && mode != fmtTypeIDName {
637                                         name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
638                                 }
639                         } else {
640                                 name = sconv(s, 0, mode)
641                         }
642                 }
643         }
644
645         if name != "" {
646                 b.WriteString(name)
647                 b.WriteString(nameSep)
648         }
649
650         if f.IsDDD() {
651                 var et *Type
652                 if f.Type != nil {
653                         et = f.Type.Elem()
654                 }
655                 b.WriteString("...")
656                 tconv2(b, et, 0, mode, visited)
657         } else {
658                 tconv2(b, f.Type, 0, mode, visited)
659         }
660
661         if verb != 'S' && !isParam && f.Note != "" {
662                 b.WriteString(" ")
663                 b.WriteString(strconv.Quote(f.Note))
664         }
665 }
666
667 // SplitVargenSuffix returns name split into a base string and a ·N
668 // suffix, if any.
669 func SplitVargenSuffix(name string) (base, suffix string) {
670         i := len(name)
671         for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
672                 i--
673         }
674         const dot = "·"
675         if i >= len(dot) && name[i-len(dot):i] == dot {
676                 i -= len(dot)
677                 return name[:i], name[i:]
678         }
679         return name, ""
680 }
681
682 // TypeHash computes a hash value for type t to use in type switch statements.
683 func TypeHash(t *Type) uint32 {
684         p := t.LinkString()
685
686         // Using SHA256 is overkill, but reduces accidental collisions.
687         h := notsha256.Sum256([]byte(p))
688         return binary.LittleEndian.Uint32(h[:4])
689 }