]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/types2/typestring.go
[dev.typeparams] all: merge master (912f075) into dev.typeparams
[gostls13.git] / src / cmd / compile / internal / types2 / typestring.go
1 // Copyright 2013 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 // This file implements printing of types.
6
7 package types2
8
9 import (
10         "bytes"
11         "fmt"
12         "unicode/utf8"
13 )
14
15 // A Qualifier controls how named package-level objects are printed in
16 // calls to TypeString, ObjectString, and SelectionString.
17 //
18 // These three formatting routines call the Qualifier for each
19 // package-level object O, and if the Qualifier returns a non-empty
20 // string p, the object is printed in the form p.O.
21 // If it returns an empty string, only the object name O is printed.
22 //
23 // Using a nil Qualifier is equivalent to using (*Package).Path: the
24 // object is qualified by the import path, e.g., "encoding/json.Marshal".
25 //
26 type Qualifier func(*Package) string
27
28 // RelativeTo returns a Qualifier that fully qualifies members of
29 // all packages other than pkg.
30 func RelativeTo(pkg *Package) Qualifier {
31         if pkg == nil {
32                 return nil
33         }
34         return func(other *Package) string {
35                 if pkg == other {
36                         return "" // same package; unqualified
37                 }
38                 return other.Path()
39         }
40 }
41
42 // If gcCompatibilityMode is set, printing of types is modified
43 // to match the representation of some types in the gc compiler:
44 //
45 //      - byte and rune lose their alias name and simply stand for
46 //        uint8 and int32 respectively
47 //      - embedded interfaces get flattened (the embedding info is lost,
48 //        and certain recursive interface types cannot be printed anymore)
49 //
50 // This makes it easier to compare packages computed with the type-
51 // checker vs packages imported from gc export data.
52 //
53 // Caution: This flag affects all uses of WriteType, globally.
54 // It is only provided for testing in conjunction with
55 // gc-generated data.
56 //
57 // This flag is exported in the x/tools/go/types package. We don't
58 // need it at the moment in the std repo and so we don't export it
59 // anymore. We should eventually try to remove it altogether.
60 // TODO(gri) remove this
61 var gcCompatibilityMode bool
62
63 // TypeString returns the string representation of typ.
64 // The Qualifier controls the printing of
65 // package-level objects, and may be nil.
66 func TypeString(typ Type, qf Qualifier) string {
67         var buf bytes.Buffer
68         WriteType(&buf, typ, qf)
69         return buf.String()
70 }
71
72 // WriteType writes the string representation of typ to buf.
73 // The Qualifier controls the printing of
74 // package-level objects, and may be nil.
75 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
76         writeType(buf, typ, qf, make([]Type, 0, 8))
77 }
78
79 // instanceMarker is the prefix for an instantiated type
80 // in "non-evaluated" instance form.
81 const instanceMarker = '#'
82
83 func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
84         // Theoretically, this is a quadratic lookup algorithm, but in
85         // practice deeply nested composite types with unnamed component
86         // types are uncommon. This code is likely more efficient than
87         // using a map.
88         for _, t := range visited {
89                 if t == typ {
90                         fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
91                         return
92                 }
93         }
94         visited = append(visited, typ)
95
96         switch t := typ.(type) {
97         case nil:
98                 buf.WriteString("<nil>")
99
100         case *Basic:
101                 // exported basic types go into package unsafe
102                 // (currently this is just unsafe.Pointer)
103                 if isExported(t.name) {
104                         if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
105                                 writeTypeName(buf, obj, qf)
106                                 break
107                         }
108                 }
109
110                 if gcCompatibilityMode {
111                         // forget the alias names
112                         switch t.kind {
113                         case Byte:
114                                 t = Typ[Uint8]
115                         case Rune:
116                                 t = Typ[Int32]
117                         }
118                 }
119                 buf.WriteString(t.name)
120
121         case *Array:
122                 fmt.Fprintf(buf, "[%d]", t.len)
123                 writeType(buf, t.elem, qf, visited)
124
125         case *Slice:
126                 buf.WriteString("[]")
127                 writeType(buf, t.elem, qf, visited)
128
129         case *Struct:
130                 buf.WriteString("struct{")
131                 for i, f := range t.fields {
132                         if i > 0 {
133                                 buf.WriteString("; ")
134                         }
135                         // This doesn't do the right thing for embedded type
136                         // aliases where we should print the alias name, not
137                         // the aliased type (see issue #44410).
138                         if !f.embedded {
139                                 buf.WriteString(f.name)
140                                 buf.WriteByte(' ')
141                         }
142                         writeType(buf, f.typ, qf, visited)
143                         if tag := t.Tag(i); tag != "" {
144                                 fmt.Fprintf(buf, " %q", tag)
145                         }
146                 }
147                 buf.WriteByte('}')
148
149         case *Pointer:
150                 buf.WriteByte('*')
151                 writeType(buf, t.base, qf, visited)
152
153         case *Tuple:
154                 writeTuple(buf, t, false, qf, visited)
155
156         case *Signature:
157                 buf.WriteString("func")
158                 writeSignature(buf, t, qf, visited)
159
160         case *Union:
161                 if t.IsEmpty() {
162                         buf.WriteString("⊥")
163                         break
164                 }
165                 for i, e := range t.types {
166                         if i > 0 {
167                                 buf.WriteString("|")
168                         }
169                         if t.tilde[i] {
170                                 buf.WriteByte('~')
171                         }
172                         writeType(buf, e, qf, visited)
173                 }
174
175         case *Interface:
176                 // We write the source-level methods and embedded types rather
177                 // than the actual method set since resolved method signatures
178                 // may have non-printable cycles if parameters have embedded
179                 // interface types that (directly or indirectly) embed the
180                 // current interface. For instance, consider the result type
181                 // of m:
182                 //
183                 //     type T interface{
184                 //         m() interface{ T }
185                 //     }
186                 //
187                 buf.WriteString("interface{")
188                 empty := true
189                 if gcCompatibilityMode {
190                         // print flattened interface
191                         // (useful to compare against gc-generated interfaces)
192                         tset := t.typeSet()
193                         for i, m := range tset.methods {
194                                 if i > 0 {
195                                         buf.WriteString("; ")
196                                 }
197                                 buf.WriteString(m.name)
198                                 writeSignature(buf, m.typ.(*Signature), qf, visited)
199                                 empty = false
200                         }
201                         if !empty && tset.types != nil {
202                                 buf.WriteString("; ")
203                         }
204                         if tset.types != nil {
205                                 buf.WriteString("type ")
206                                 writeType(buf, tset.types, qf, visited)
207                         }
208                 } else {
209                         // print explicit interface methods and embedded types
210                         for i, m := range t.methods {
211                                 if i > 0 {
212                                         buf.WriteString("; ")
213                                 }
214                                 buf.WriteString(m.name)
215                                 writeSignature(buf, m.typ.(*Signature), qf, visited)
216                                 empty = false
217                         }
218                         if !empty && len(t.embeddeds) > 0 {
219                                 buf.WriteString("; ")
220                         }
221                         for i, typ := range t.embeddeds {
222                                 if i > 0 {
223                                         buf.WriteString("; ")
224                                 }
225                                 writeType(buf, typ, qf, visited)
226                                 empty = false
227                         }
228                 }
229                 // print /* incomplete */ if needed to satisfy existing tests
230                 // TODO(gri) get rid of this eventually
231                 if debug && t.tset == nil {
232                         if !empty {
233                                 buf.WriteByte(' ')
234                         }
235                         buf.WriteString("/* incomplete */")
236                 }
237                 buf.WriteByte('}')
238
239         case *Map:
240                 buf.WriteString("map[")
241                 writeType(buf, t.key, qf, visited)
242                 buf.WriteByte(']')
243                 writeType(buf, t.elem, qf, visited)
244
245         case *Chan:
246                 var s string
247                 var parens bool
248                 switch t.dir {
249                 case SendRecv:
250                         s = "chan "
251                         // chan (<-chan T) requires parentheses
252                         if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
253                                 parens = true
254                         }
255                 case SendOnly:
256                         s = "chan<- "
257                 case RecvOnly:
258                         s = "<-chan "
259                 default:
260                         panic("unreachable")
261                 }
262                 buf.WriteString(s)
263                 if parens {
264                         buf.WriteByte('(')
265                 }
266                 writeType(buf, t.elem, qf, visited)
267                 if parens {
268                         buf.WriteByte(')')
269                 }
270
271         case *Named:
272                 writeTypeName(buf, t.obj, qf)
273                 if t.targs != nil {
274                         // instantiated type
275                         buf.WriteByte('[')
276                         writeTypeList(buf, t.targs, qf, visited)
277                         buf.WriteByte(']')
278                 } else if t.TParams() != nil {
279                         // parameterized type
280                         writeTParamList(buf, t.TParams(), qf, visited)
281                 }
282
283         case *TypeParam:
284                 s := "?"
285                 if t.obj != nil {
286                         // Optionally write out package for typeparams (like Named).
287                         // TODO(danscales): this is required for import/export, so
288                         // we maybe need a separate function that won't be changed
289                         // for debugging purposes.
290                         if t.obj.pkg != nil {
291                                 writePackage(buf, t.obj.pkg, qf)
292                         }
293                         s = t.obj.name
294                 }
295                 buf.WriteString(s + subscript(t.id))
296
297         case *instance:
298                 buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
299                 writeTypeName(buf, t.base.obj, qf)
300                 buf.WriteByte('[')
301                 writeTypeList(buf, t.targs, qf, visited)
302                 buf.WriteByte(']')
303
304         case *top:
305                 buf.WriteString("⊤")
306
307         default:
308                 // For externally defined implementations of Type.
309                 // Note: In this case cycles won't be caught.
310                 buf.WriteString(t.String())
311         }
312 }
313
314 func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
315         for i, typ := range list {
316                 if i > 0 {
317                         buf.WriteString(", ")
318                 }
319                 writeType(buf, typ, qf, visited)
320         }
321 }
322
323 func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
324         buf.WriteString("[")
325         var prev Type
326         for i, p := range list {
327                 // TODO(gri) support 'any' sugar here.
328                 var b Type = &emptyInterface
329                 if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
330                         b = t.bound
331                 }
332                 if i > 0 {
333                         if b != prev {
334                                 // type bound changed - write previous one before advancing
335                                 buf.WriteByte(' ')
336                                 writeType(buf, prev, qf, visited)
337                         }
338                         buf.WriteString(", ")
339                 }
340                 prev = b
341
342                 if t, _ := p.typ.(*TypeParam); t != nil {
343                         writeType(buf, t, qf, visited)
344                 } else {
345                         buf.WriteString(p.name)
346                 }
347         }
348         if prev != nil {
349                 buf.WriteByte(' ')
350                 writeType(buf, prev, qf, visited)
351         }
352         buf.WriteByte(']')
353 }
354
355 func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
356         if obj == nil {
357                 buf.WriteString("<Named w/o object>")
358                 return
359         }
360         if obj.pkg != nil {
361                 writePackage(buf, obj.pkg, qf)
362         }
363         buf.WriteString(obj.name)
364
365         if instanceHashing != 0 {
366                 // For local defined types, use the (original!) TypeName's position
367                 // to disambiguate. This is overkill, and could probably instead
368                 // just be the pointer value (if we assume a non-moving GC) or
369                 // a unique ID (like cmd/compile uses). But this works for now,
370                 // and is convenient for debugging.
371
372                 // TODO(mdempsky): I still don't fully understand why typ.orig.orig
373                 // can differ from typ.orig, or whether looping more than twice is
374                 // ever necessary.
375                 typ := obj.typ.(*Named)
376                 for typ.orig != typ {
377                         typ = typ.orig
378                 }
379                 if orig := typ.obj; orig.pkg != nil && orig.parent != orig.pkg.scope {
380                         fmt.Fprintf(buf, "@%q", orig.pos)
381                 }
382         }
383 }
384
385 func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
386         buf.WriteByte('(')
387         if tup != nil {
388                 for i, v := range tup.vars {
389                         if i > 0 {
390                                 buf.WriteString(", ")
391                         }
392                         if v.name != "" {
393                                 buf.WriteString(v.name)
394                                 buf.WriteByte(' ')
395                         }
396                         typ := v.typ
397                         if variadic && i == len(tup.vars)-1 {
398                                 if s, ok := typ.(*Slice); ok {
399                                         buf.WriteString("...")
400                                         typ = s.elem
401                                 } else {
402                                         // special case:
403                                         // append(s, "foo"...) leads to signature func([]byte, string...)
404                                         if t := asBasic(typ); t == nil || t.kind != String {
405                                                 panic("internal error: string type expected")
406                                         }
407                                         writeType(buf, typ, qf, visited)
408                                         buf.WriteString("...")
409                                         continue
410                                 }
411                         }
412                         writeType(buf, typ, qf, visited)
413                 }
414         }
415         buf.WriteByte(')')
416 }
417
418 // WriteSignature writes the representation of the signature sig to buf,
419 // without a leading "func" keyword.
420 // The Qualifier controls the printing of
421 // package-level objects, and may be nil.
422 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
423         writeSignature(buf, sig, qf, make([]Type, 0, 8))
424 }
425
426 func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
427         if sig.tparams != nil {
428                 writeTParamList(buf, sig.tparams, qf, visited)
429         }
430
431         writeTuple(buf, sig.params, sig.variadic, qf, visited)
432
433         n := sig.results.Len()
434         if n == 0 {
435                 // no result
436                 return
437         }
438
439         buf.WriteByte(' ')
440         if n == 1 && sig.results.vars[0].name == "" {
441                 // single unnamed result
442                 writeType(buf, sig.results.vars[0].typ, qf, visited)
443                 return
444         }
445
446         // multiple or named result(s)
447         writeTuple(buf, sig.results, false, qf, visited)
448 }
449
450 // subscript returns the decimal (utf8) representation of x using subscript digits.
451 func subscript(x uint64) string {
452         const w = len("₀") // all digits 0...9 have the same utf8 width
453         var buf [32 * w]byte
454         i := len(buf)
455         for {
456                 i -= w
457                 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
458                 x /= 10
459                 if x == 0 {
460                         break
461                 }
462         }
463         return string(buf[i:])
464 }